// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include "Common/BitSet.h"
#include "Common/CommonTypes.h"

// Vertex array numbers
enum
{
  ARRAY_POSITION = 0,
  ARRAY_NORMAL = 1,
  ARRAY_COLOR = 2,
  ARRAY_COLOR2 = 3,
  ARRAY_TEXCOORD0 = 4,
};

// Vertex components
enum
{
  NOT_PRESENT = 0,
  DIRECT = 1,
  INDEX8 = 2,
  INDEX16 = 3,

  MASK_INDEXED = 2,
};

enum
{
  FORMAT_UBYTE = 0,  // 2 Cmp
  FORMAT_BYTE = 1,   // 3 Cmp
  FORMAT_USHORT = 2,
  FORMAT_SHORT = 3,
  FORMAT_FLOAT = 4,
};

enum
{
  FORMAT_16B_565 = 0,  // NA
  FORMAT_24B_888 = 1,
  FORMAT_32B_888x = 2,
  FORMAT_16B_4444 = 3,
  FORMAT_24B_6666 = 4,
  FORMAT_32B_8888 = 5,
};

#pragma pack(4)
union TVtxDesc
{
  u64 Hex;
  struct
  {
    // 0: not present
    // 1: present
    u64 PosMatIdx : 1;
    u64 Tex0MatIdx : 1;
    u64 Tex1MatIdx : 1;
    u64 Tex2MatIdx : 1;
    u64 Tex3MatIdx : 1;
    u64 Tex4MatIdx : 1;
    u64 Tex5MatIdx : 1;
    u64 Tex6MatIdx : 1;
    u64 Tex7MatIdx : 1;

    // 00: not present
    // 01: direct
    // 10: 8 bit index
    // 11: 16 bit index
    u64 Position : 2;
    u64 Normal : 2;
    u64 Color0 : 2;
    u64 Color1 : 2;
    u64 Tex0Coord : 2;
    u64 Tex1Coord : 2;
    u64 Tex2Coord : 2;
    u64 Tex3Coord : 2;
    u64 Tex4Coord : 2;
    u64 Tex5Coord : 2;
    u64 Tex6Coord : 2;
    u64 Tex7Coord : 2;
    u64 : 31;
  };

  struct
  {
    u32 Hex0, Hex1;
  };

  // Easily index into the Position..Tex7Coord fields.
  u32 GetVertexArrayStatus(int idx) { return (Hex >> (9 + idx * 2)) & 0x3; }
};

union UVAT_group0
{
  u32 Hex;
  struct
  {
    // 0:8
    u32 PosElements : 1;
    u32 PosFormat : 3;
    u32 PosFrac : 5;
    // 9:12
    u32 NormalElements : 1;
    u32 NormalFormat : 3;
    // 13:16
    u32 Color0Elements : 1;
    u32 Color0Comp : 3;
    // 17:20
    u32 Color1Elements : 1;
    u32 Color1Comp : 3;
    // 21:29
    u32 Tex0CoordElements : 1;
    u32 Tex0CoordFormat : 3;
    u32 Tex0Frac : 5;
    // 30:31
    u32 ByteDequant : 1;
    u32 NormalIndex3 : 1;
  };
};

union UVAT_group1
{
  u32 Hex;
  struct
  {
    // 0:8
    u32 Tex1CoordElements : 1;
    u32 Tex1CoordFormat : 3;
    u32 Tex1Frac : 5;
    // 9:17
    u32 Tex2CoordElements : 1;
    u32 Tex2CoordFormat : 3;
    u32 Tex2Frac : 5;
    // 18:26
    u32 Tex3CoordElements : 1;
    u32 Tex3CoordFormat : 3;
    u32 Tex3Frac : 5;
    // 27:30
    u32 Tex4CoordElements : 1;
    u32 Tex4CoordFormat : 3;
    //
    u32 : 1;
  };
};

union UVAT_group2
{
  u32 Hex;
  struct
  {
    // 0:4
    u32 Tex4Frac : 5;
    // 5:13
    u32 Tex5CoordElements : 1;
    u32 Tex5CoordFormat : 3;
    u32 Tex5Frac : 5;
    // 14:22
    u32 Tex6CoordElements : 1;
    u32 Tex6CoordFormat : 3;
    u32 Tex6Frac : 5;
    // 23:31
    u32 Tex7CoordElements : 1;
    u32 Tex7CoordFormat : 3;
    u32 Tex7Frac : 5;
  };
};

struct ColorAttr
{
  u8 Elements;
  u8 Comp;
};

struct TexAttr
{
  u8 Elements;
  u8 Format;
  u8 Frac;
};

struct TVtxAttr
{
  u8 PosElements;
  u8 PosFormat;
  u8 PosFrac;
  u8 NormalElements;
  u8 NormalFormat;
  ColorAttr color[2];
  TexAttr texCoord[8];
  bool ByteDequant;
  u8 NormalIndex3;
};

// Matrix indices
union TMatrixIndexA
{
  struct
  {
    u32 PosNormalMtxIdx : 6;
    u32 Tex0MtxIdx : 6;
    u32 Tex1MtxIdx : 6;
    u32 Tex2MtxIdx : 6;
    u32 Tex3MtxIdx : 6;
  };
  struct
  {
    u32 Hex : 30;
    u32 unused : 2;
  };
};

union TMatrixIndexB
{
  struct
  {
    u32 Tex4MtxIdx : 6;
    u32 Tex5MtxIdx : 6;
    u32 Tex6MtxIdx : 6;
    u32 Tex7MtxIdx : 6;
  };
  struct
  {
    u32 Hex : 24;
    u32 unused : 8;
  };
};

#pragma pack()

struct VAT
{
  UVAT_group0 g0;
  UVAT_group1 g1;
  UVAT_group2 g2;
};

class VertexLoaderBase;

// STATE_TO_SAVE
struct CPState final
{
  u32 array_bases[16];
  u32 array_strides[16];
  TMatrixIndexA matrix_index_a;
  TMatrixIndexB matrix_index_b;
  TVtxDesc vtx_desc;
  // Most games only use the first VtxAttr and simply reconfigure it all the time as needed.
  VAT vtx_attr[8];

  // Attributes that actually belong to VertexLoaderManager:
  BitSet32 attr_dirty;
  bool bases_dirty;
  VertexLoaderBase* vertex_loaders[8];
  int last_id;
};

class PointerWrap;

extern CPState g_main_cp_state;
extern CPState g_preprocess_cp_state;

// Might move this into its own file later.
void LoadCPReg(u32 SubCmd, u32 Value, bool is_preprocess = false);

// Fills memory with data from CP regs
void FillCPMemoryArray(u32* memory);

void DoCPState(PointerWrap& p);

void CopyPreprocessCPStateFromMain();