#include "texture.hpp"

#include "systembus.hpp"

#include "holly/holly.hpp"
#include "holly/core_bits.hpp"
#include "holly/texture_memory_alloc9.hpp"
#include "holly/ta_fifo_texture_memory_transfer.hpp"

#include "font/ter_u12n.data.h"
#include "font/ter_u32n.data.h"
#include "texture/igh25_box_top_32.data.h"
#include "texture/igh25_box_bottom_32.data.h"
#include "texture/igh25_box_side_32.data.h"
#include "texture/walking/frame0000_128.data.h"
#include "texture/walking/frame0001_128.data.h"
#include "texture/walking/frame0002_128.data.h"
#include "texture/walking/frame0003_128.data.h"
#include "texture/walking/frame0004_128.data.h"
#include "texture/walking/frame0005_128.data.h"
#include "texture/walking/frame0006_128.data.h"
#include "texture/turning/frame0000_128.data.h"
#include "texture/turning/frame0001_128.data.h"
#include "texture/turning/frame0002_128.data.h"
#include "texture/turning/frame0003_128.data.h"
#include "texture/turning/frame0004_128.data.h"
#include "texture/turning/frame0005_128.data.h"
#include "texture/turning/frame0006_128.data.h"
#include "cover/clocks.data.h"
#include "cover/mossycottage.data.h"
#include "cover/mountain.data.h"
#include "cover/thebeach.data.h"
#include "cover/tree.data.h"
#include "cover/redtree.data.h"
#include "cover/silvertrees.data.h"

#include "assert.h"

namespace texture {
  struct entry {
    void * start;
    int size;
    int offset;
  };

  const entry textures[] = {
    {
      .start = reinterpret_cast<void *>(&_binary_font_ter_u12n_data_start),
      .size = reinterpret_cast<int>(&_binary_font_ter_u12n_data_size),
      .offset = offset::ter_u12n,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_font_ter_u32n_data_start),
      .size = reinterpret_cast<int>(&_binary_font_ter_u32n_data_size),
      .offset = offset::ter_u32n,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_igh25_box_top_32_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_igh25_box_top_32_data_size),
      .offset = offset::igh25_box_top_32,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_igh25_box_bottom_32_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_igh25_box_bottom_32_data_size),
      .offset = offset::igh25_box_bottom_32,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_igh25_box_side_32_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_igh25_box_side_32_data_size),
      .offset = offset::igh25_box_side_32,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_walking_frame0000_128_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_walking_frame0000_128_data_size),
      .offset = offset::walking_frame0000,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_walking_frame0001_128_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_walking_frame0001_128_data_size),
      .offset = offset::walking_frame0001,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_walking_frame0002_128_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_walking_frame0002_128_data_size),
      .offset = offset::walking_frame0002,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_walking_frame0003_128_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_walking_frame0003_128_data_size),
      .offset = offset::walking_frame0003,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_walking_frame0004_128_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_walking_frame0004_128_data_size),
      .offset = offset::walking_frame0004,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_walking_frame0005_128_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_walking_frame0005_128_data_size),
      .offset = offset::walking_frame0005,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_walking_frame0006_128_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_walking_frame0006_128_data_size),
      .offset = offset::walking_frame0006,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_turning_frame0000_128_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_turning_frame0000_128_data_size),
      .offset = offset::turning_frame0000,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_turning_frame0001_128_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_turning_frame0001_128_data_size),
      .offset = offset::turning_frame0001,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_turning_frame0002_128_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_turning_frame0002_128_data_size),
      .offset = offset::turning_frame0002,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_turning_frame0003_128_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_turning_frame0003_128_data_size),
      .offset = offset::turning_frame0003,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_turning_frame0004_128_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_turning_frame0004_128_data_size),
      .offset = offset::turning_frame0004,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_turning_frame0005_128_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_turning_frame0005_128_data_size),
      .offset = offset::turning_frame0005,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_texture_turning_frame0006_128_data_start),
      .size = reinterpret_cast<int>(&_binary_texture_turning_frame0006_128_data_size),
      .offset = offset::turning_frame0006,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_cover_clocks_data_start),
      .size = reinterpret_cast<int>(&_binary_cover_clocks_data_size),
      .offset = offset::clocks,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_cover_mossycottage_data_start),
      .size = reinterpret_cast<int>(&_binary_cover_mossycottage_data_size),
      .offset = offset::mossycottage,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_cover_mountain_data_start),
      .size = reinterpret_cast<int>(&_binary_cover_mountain_data_size),
      .offset = offset::mountain,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_cover_thebeach_data_start),
      .size = reinterpret_cast<int>(&_binary_cover_thebeach_data_size),
      .offset = offset::thebeach,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_cover_tree_data_start),
      .size = reinterpret_cast<int>(&_binary_cover_tree_data_size),
      .offset = offset::tree,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_cover_redtree_data_start),
      .size = reinterpret_cast<int>(&_binary_cover_redtree_data_size),
      .offset = offset::redtree,
    },
    {
      .start = reinterpret_cast<void *>(&_binary_cover_silvertrees_data_start),
      .size = reinterpret_cast<int>(&_binary_cover_silvertrees_data_size),
      .offset = offset::silvertrees,
    },
  };

  const int textures_length = (sizeof (textures)) / (sizeof (textures[0]));

  static inline void transfer_palettes()
  {
    holly.PAL_RAM_CTRL = pal_ram_ctrl::pixel_format::argb4444;

    holly.PALETTE_RAM[0] = 0;
    holly.PALETTE_RAM[1] = 0xffff;

    holly.PT_ALPHA_REF = 0xf0;
  }

  void init()
  {
    system.LMMODE0 = 0; // 64-bit address space
    system.LMMODE1 = 0; // 64-bit address space

    for (int i = 0; i < textures_length; i++) {
      uint32_t offset = texture_memory_alloc.texture.start + textures[i].offset;
      void * dst = reinterpret_cast<void *>(&ta_fifo_texture_memory[offset / 4]);
      void * src = textures[i].start;
      int size = textures[i].size;

      ta_fifo_texture_memory_transfer::copy(dst, src, size);
      assert((int)offset + size < 8 * 1024 * 1024);
    }

    transfer_palettes();
  }
}
