#include "xentax.h"
#include "stdres.h"
#include "ps3_gundam_crossfire.h"
#include "xb360_onechanbara2.h"

INT_PTR CALLBACK RipperProc(HWND dialog, UINT message, WPARAM wparam, LPARAM lparam);
bool is_face_flipped(const float* v1, const float* v2, const float* v3, const float* n1, const float* n2, const float* n3);

bool Test(void)
{
 ifstream ifile("anna1u.chr", ios::binary);
 if(!ifile) return false;

 ifile.seekg(0x14);
 uint32 vbuffer_offset = LE_read_uint32(ifile);
 uint32 ibuffer_offset = LE_read_uint32(ifile);
 uint32 tbuffer_offset = LE_read_uint32(ifile);

 // calculate vertex buffer properties
 uint32 vbuffer_size = ibuffer_offset - vbuffer_offset;
 uint32 vertices = vbuffer_size/32;
 cout << "size of vertex buffer in bytes = " << vbuffer_size << endl;
 cout << "number of vertices = " << vertices << endl;
 cout << endl;

 // calculate index buffer properties
 uint32 ibuffer_size = tbuffer_offset - ibuffer_offset;
 uint32 indices = ibuffer_size/2;
 cout << "size of index buffer in bytes = " << ibuffer_size << endl;
 cout << "number of indices = " << indices << endl;
 cout << endl;

 // read number of textures
 ifile.seekg(0x2C);
 uint32 n_textures = LE_read_uint32(ifile);
 cout << "number of textures = " << n_textures << endl;

 // surface data
 deque<size_t> stripsizes;
 deque<size_t> stripimage;
 deque<bool> stripeffect;

 // read surfaces
 uint32 curr_surface = 0;
 ifile.seekg(0x40);
 for(;;)
    {
     uint32 temp1 = LE_read_uint32(ifile);
     if(temp1 == 0)
       {
        uint32 h01 = temp1;
        uint32 h02 = LE_read_uint32(ifile);
        uint32 h03 = LE_read_uint16(ifile);
        uint32 h04 = LE_read_uint16(ifile);
        cout << "h01 = " << h01 << endl;
        cout << "h02 = " << h02 << endl;
        cout << "h03 = " << h03 << endl;
        cout << "h04 = " << h04 << endl;

        // read a bunch of half-floats
        LE_read_float16(ifile);
        LE_read_float16(ifile);
        LE_read_float16(ifile);
        LE_read_float16(ifile);
        LE_read_float16(ifile);
        LE_read_float16(ifile);
        LE_read_float16(ifile);
        LE_read_float16(ifile);
       }
     else if(temp1 == 1)
       {
        cout << "surface " << curr_surface++ << endl;
        uint32 param01 = LE_read_uint32(ifile);
        uint32 param02 = LE_read_uint32(ifile);
        uint16 param03 = LE_read_uint16(ifile); // usually 68
        uint16 param04 = LE_read_uint16(ifile);
        uint32 param05 = LE_read_uint32(ifile);
        uint32 param06 = LE_read_uint32(ifile);
        uint32 param07 = LE_read_uint32(ifile);
        uint32 param08 = LE_read_uint32(ifile);
        uint32 param09 = LE_read_uint32(ifile);
        uint32 param10 = LE_read_uint32(ifile);
        uint32 param11 = LE_read_uint32(ifile); // texture
        uint32 param12 = LE_read_uint32(ifile);
        uint32 param13 = LE_read_uint32(ifile);
        uint32 param14 = LE_read_uint32(ifile); // number of indices to use
        stripeffect.push_back((param05 == 3));
        stripimage.push_back(param11);
        stripsizes.push_back(param14);

        cout << param01 << endl;
        cout << param02 << endl;
        cout << param03 << endl; // usually 68
        cout << param04 << endl;
        cout << param05 << endl;
        cout << param06 << endl;
        cout << param07 << endl;
        cout << param08 << endl;
        cout << param09 << endl;
        cout << param10 << endl;
        cout << param11 << endl;
        cout << param12 << endl;
        cout << param13 << endl;
        cout << param14 << endl;
        cout << endl;
       }
     else if(temp1 == 2)
       {
        cout << "surface " << curr_surface++ << endl;
        uint32 param01 = LE_read_uint32(ifile); // texture
        uint32 param02 = LE_read_uint32(ifile);
        uint32 param03 = LE_read_uint32(ifile);
        uint32 param04 = LE_read_uint32(ifile);
        stripeffect.push_back(false);
        stripimage.push_back(param01);
        stripsizes.push_back(param04);

        cout << param01 << endl;
        cout << param02 << endl;
        cout << param03 << endl; // usually 68
        cout << param04 << endl;
        cout << endl;
       }
     else if(temp1 == 0xFFFFFFFF)
       {
        // marks last surface?
       }
     else
        break;
    }

 // create OBJ file
 ofstream ofile("output.obj");
 ofile << "o output.obj" << endl;
 ofile << "mtllib " << "output" << ".mtl" << endl;

 // move file pointer to vertex buffer
 ifile.seekg(vbuffer_offset);

 // create vertex buffer
 VERTEX_BUFFER vbuffer;
 vbuffer.flags = VERTEX_POSITION | VERTEX_NORMAL | VERTEX_UV;
 vbuffer.elem = vertices;
 vbuffer.data.reset(new VERTEX[vbuffer.elem]);

 // read vertex data
 for(uint32 i = 0; i < vertices; i++)
    {
     // read vertex data
     VERTEX v;
     v.vx = LE_read_float16(ifile);
     v.vy = LE_read_float16(ifile);
     v.vz = LE_read_float16(ifile);
            LE_read_float16(ifile);
     v.nx = LE_read_float16(ifile);
     v.ny = LE_read_float16(ifile);
     v.nz = LE_read_float16(ifile);
            LE_read_float16(ifile);
     v.tu = LE_read_float16(ifile);
     v.tv = 1.0f - LE_read_float16(ifile);
            LE_read_float16(ifile);
            LE_read_float16(ifile);
            LE_read_float16(ifile);
            LE_read_float16(ifile);
            LE_read_float16(ifile);
            LE_read_float16(ifile);

     // save vertex data
     vbuffer.data[i] = v;

     // save vertex data
     ofile << "v " << v.vx << " " << v.vy << " " << v.vz << endl;
     ofile << "vn " << v.nx << " " << v.ny << " " << v.nz << endl;
     ofile << "vt " << v.tu << " " << v.tv << endl;
    }

 // move to index data
 ifile.seekg(ibuffer_offset);

 // read index data
 boost::shared_array<uint16> ibuffer(new uint16[indices]);
 LE_read_array(ifile, ibuffer.get(), indices);

 // 
 uint32 sum = 0;
 for(size_t i = 0; i < stripsizes.size(); i++) sum += stripsizes[i];
 cout << "sum = " << sum << endl;
 cout << "indices = " << indices << endl;
 uint32 leftover = indices - sum;
 cout << "leftover = " << leftover << endl;
 cout << endl;

 // process surfaces
 uint32 offset = 0;
 for(size_t i = 0; i < stripsizes.size(); i++)
    {
     uint32 indices = stripsizes[i];
     cout << "surface indices = " << indices << endl;

     if(!stripeffect[i]) // TEST
     {

     // save group
     ofile << "g " << "surface_" << setfill('0') << setw(3) << i << endl;
     ofile << "usemtl " << "surface_" << setfill('0') << setw(3) << i << endl;

     // save first triangle
     uint32 a = ibuffer[offset + 0] + 1;
     uint32 b = ibuffer[offset + 1] + 1;
     uint32 c = ibuffer[offset + 2] + 1;

     float v1[3] = { vbuffer.data[a - 1].vx, vbuffer.data[a - 1].vy, vbuffer.data[a - 1].vz };
     float v2[3] = { vbuffer.data[b - 1].vx, vbuffer.data[b - 1].vy, vbuffer.data[b - 1].vz };
     float v3[3] = { vbuffer.data[c - 1].vx, vbuffer.data[c - 1].vy, vbuffer.data[c - 1].vz };
     float n1[3] = { vbuffer.data[a - 1].nx, vbuffer.data[a - 1].ny, vbuffer.data[a - 1].nz };
     float n2[3] = { vbuffer.data[b - 1].nx, vbuffer.data[b - 1].ny, vbuffer.data[b - 1].nz };
     float n3[3] = { vbuffer.data[c - 1].nx, vbuffer.data[c - 1].ny, vbuffer.data[c - 1].nz };

     if(is_face_flipped(v1, v2, v3, n1, n2, n3)) {
        ofile << "f " << a << "/" << a << "/" << a << " " <<
                         c << "/" << c << "/" << c << " " <<
                         b << "/" << b << "/" << b << endl;
       }
     else {
        ofile << "f " << a << "/" << a << "/" << a << " " <<
                         b << "/" << b << "/" << b << " " <<
                         c << "/" << c << "/" << c << endl;
       }

     // save other triangles
     for(size_t j = 3; j < stripsizes[i]; j++)
        {
         a = b;
         b = c;
         c = ibuffer[offset + j] + 1;
     
         // skip degenerate triangles
         if(a == b || a == c || b == c)
            continue;
     
         if(j % 2)
           {
            float v1[3] = { vbuffer.data[a - 1].vx, vbuffer.data[a - 1].vy, vbuffer.data[a - 1].vz };
            float v2[3] = { vbuffer.data[c - 1].vx, vbuffer.data[c - 1].vy, vbuffer.data[c - 1].vz };
            float v3[3] = { vbuffer.data[b - 1].vx, vbuffer.data[b - 1].vy, vbuffer.data[b - 1].vz };
            float n1[3] = { vbuffer.data[a - 1].nx, vbuffer.data[a - 1].ny, vbuffer.data[a - 1].nz };
            float n2[3] = { vbuffer.data[c - 1].nx, vbuffer.data[c - 1].ny, vbuffer.data[c - 1].nz };
            float n3[3] = { vbuffer.data[b - 1].nx, vbuffer.data[b - 1].ny, vbuffer.data[b - 1].nz };

            if(is_face_flipped(v1, v2, v3, n1, n2, n3)) {
               ofile << "f " << a << "/" << a << "/" << a << " " <<
                                b << "/" << b << "/" << b << " " <<
                                c << "/" << c << "/" << c << endl;
              }
            else {
               ofile << "f " << a << "/" << a << "/" << a << " " <<
                                c << "/" << c << "/" << c << " " <<
                                b << "/" << b << "/" << b << endl;
              }
           }
         else
           {
            float v1[3] = { vbuffer.data[a - 1].vx, vbuffer.data[a - 1].vy, vbuffer.data[a - 1].vz };
            float v2[3] = { vbuffer.data[b - 1].vx, vbuffer.data[b - 1].vy, vbuffer.data[b - 1].vz };
            float v3[3] = { vbuffer.data[c - 1].vx, vbuffer.data[c - 1].vy, vbuffer.data[c - 1].vz };
            float n1[3] = { vbuffer.data[a - 1].nx, vbuffer.data[a - 1].ny, vbuffer.data[a - 1].nz };
            float n2[3] = { vbuffer.data[b - 1].nx, vbuffer.data[b - 1].ny, vbuffer.data[b - 1].nz };
            float n3[3] = { vbuffer.data[c - 1].nx, vbuffer.data[c - 1].ny, vbuffer.data[c - 1].nz };

            if(is_face_flipped(v1, v2, v3, n1, n2, n3)) {
               ofile << "f " << a << "/" << a << "/" << a << " " <<
                                c << "/" << c << "/" << c << " " <<
                                b << "/" << b << "/" << b << endl;
              }
            else {
               ofile << "f " << a << "/" << a << "/" << a << " " <<
                                b << "/" << b << "/" << b << " " <<
                                c << "/" << c << "/" << c << endl;
              }
           }
        }

     } // TEST

     // move offset
     offset += stripsizes[i];
    }

 // save materials
 ofstream mtlfile("output.mtl");
 if(!mtlfile) return error("OBJ EXPORT: Error creating material file.");
 for(size_t i = 0; i < stripsizes.size(); i++) {
     mtlfile << "newmtl " << "surface_" << setfill('0') << setw(3) << i << endl;
     mtlfile << "Ka 0 0 0" << endl;
     mtlfile << "Kd 0.784314 0.784314 0.784314" << endl;
     mtlfile << "Ks 0 0 0" << endl;
     mtlfile << "Ni 1" << endl;
     mtlfile << "Ns 400" << endl;
     mtlfile << "Tf 1 1 1" << endl;
     mtlfile << "d 1" << endl;
     mtlfile << "map_Kd texture_" << setfill('0') << setw(3) << stripimage[i] << ".png" << endl;
     mtlfile << endl;
    }

 // move to texture data
 ifile.seekg(tbuffer_offset);

 struct TEXTUREINFO { uint32 p1, p2, p3, p4; };
 deque<TEXTUREINFO> tinfo;

 // read texture info
 for(size_t i = 0; i < n_textures; i++)
    {
     TEXTUREINFO item;
     item.p1 = LE_read_uint32(ifile);
     item.p2 = LE_read_uint32(ifile);
     item.p3 = LE_read_uint32(ifile);
     item.p4 = LE_read_uint32(ifile);
     tinfo.push_back(item);
    }

 // read texture data
 for(size_t i = 0; i < n_textures; i++)
    {
     TEXTUREINFO item = tinfo[i];

     // 
     stringstream ss;
     ss << "texture_" << setfill('0') << setw(3) << i << ".dds";
     ofstream ddsfile(ss.str().c_str(), ios::binary);
     ddsfile.write("DDS ", 4);

     // save DDS header
     DDS_HEADER header;
     CreateUncompressedDDSHeader(item.p1, item.p2, 0, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, FALSE, &header);
     ddsfile.write((char*)&header, sizeof(header));

     // save DDS data
     boost::shared_array<char> data(new char[item.p3]);
     ifile.read(data.get(), item.p3);
     ddsfile.write(data.get(), item.p3);
    }

 return true;
}

void Test2(void)
{
 // negate z to convert to right-hand space
 double points[7][3] = {
  { -3.38441, 147.212, 2.82783 }, // 0
  { -4.17231, 147.948, 2.17650 }, // 1
  { -3.65146, 147.122, 2.01224 }, // 2
  { -3.98159, 147.350, 1.01742 }, // 50
  { -4.53017, 148.112, 1.25919 }, // 62
  { -3.84511, 148.011, 2.93451 }, // 63
  { -4.57004, 148.829, 2.43792 }, // 64
 };

 // triangle
 double v1[3] = { 26.3867, 136.591, -1.16695 };
 double v2[3] = { 26.304, 138.489, -1.08023 };
 double v3[3] = { 23.769, 136.48, -1.12714 };

 // compute a
 double a[3] = {
  v2[0] - v1[0],
  v2[1] - v1[1],
  v2[2] - v1[2],
 };
 double da = std::sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]);
 a[0] /= da;
 a[1] /= da;
 a[2] /= da;

 // compute b
 double b[3] = {
  v3[0] - v1[0],
  v3[1] - v1[1],
  v3[2] - v1[2],
 };
 double db = std::sqrt(b[0]*b[0] + b[1]*b[1] + b[2]*b[2]);
 b[0] /= db;
 b[1] /= db;
 b[2] /= db;

 // compute c
 double c[3] = {
  a[1]*b[2] - a[2]*b[1],
  a[2]*b[0] - a[0]*b[2],
  a[0]*b[1] - a[1]*b[0],
 };
 double dc = std::sqrt(c[0]*c[0] + c[1]*c[1] + c[2]*c[2]);
 c[0] /= dc;
 c[1] /= dc;
 c[2] /= dc;

 cout << "normal = " << c[0] << "," << c[1] << "," << c[2] << endl;

 // compute d
 double d[3] = {
  -6,
   94,
   -123,
 };
 double dd = std::sqrt(d[0]*d[0] + d[1]*d[1] + d[2]*d[2]);
 d[0] /= dd;
 d[1] /= dd;
 d[2] /= dd;
 cout << "normal = " << d[0] << "," << d[1] << "," << d[2] << endl;
}

int main()
{
 //PS3::GundamCrossfire::extract();
 XB360::Onechanbara2::extract();

/*
 ifstream ifile("nina1u.kmd", ios::binary);

 LE_read_uint32(ifile);
 LE_read_uint32(ifile);
 LE_read_uint32(ifile); // 98
 LE_read_uint32(ifile); // ??
 LE_read_uint32(ifile); // 8
 LE_read_uint32(ifile); // 0xDC0 + 0x10 = offset to data
 LE_read_uint32(ifile); // 0xC9F
 LE_read_uint32(ifile); // 0x1FBC
 LE_read_uint32(ifile); // 0x5F34
 LE_read_uint32(ifile); // 0x581
 LE_read_uint32(ifile); // 0x2ABE
 LE_read_uint32(ifile); // 0
 LE_read_uint32(ifile); // 0
 LE_read_uint32(ifile); // 0xD0D
 LE_read_uint32(ifile); // 0
 LE_read_uint32(ifile); // 0
 LE_read_uint32(ifile); // 0
 LE_read_uint32(ifile); // 0
 LE_read_uint32(ifile); // 0xB9D60 = offset
 LE_read_uint32(ifile); // 0
 LE_read_uint32(ifile); // 0
 LE_read_uint32(ifile); // 0
 LE_read_uint32(ifile); // 0
 LE_read_uint32(ifile); // 0xFC160
 LE_read_uint32(ifile); // 0x1070A0
 LE_read_uint32(ifile); // 0x1077A0
 LE_read_uint32(ifile); // 0
 LE_read_uint32(ifile); // 0
 LE_read_uint32(ifile); // 0
 LE_read_uint32(ifile); // 0
 LE_read_uint32(ifile); // 0
 LE_read_uint32(ifile); // 0xFFFFFFFF

 ofstream ofile("test.obj");
 ofile << "o test.obj" << endl;

 struct bone {
  uint32 parent;
  float x;
  float y;
  float z;
  float x_abs;
  float y_abs;
  float z_abs;
 };
 map<uint32, bone> bonemap;
 typedef map<uint32, bone>::value_type boneitem;
 typedef map<uint32, bone>::iterator bone_iterator;

 ifile.seekg(0x7C);
 size_t max_parent = 0;
 for(size_t i = 0; i < 98; i++)
    {
     uint32 parent = LE_read_uint32(ifile);
     float x = LE_read_float32(ifile);
     float y = LE_read_float32(ifile);
     float z = LE_read_float32(ifile);
     float sx = LE_read_float32(ifile);
     float sy = LE_read_float32(ifile);
     float sz = LE_read_float32(ifile);
     LE_read_uint32(ifile);
     cout << endl;

     bone b;
     b.parent = parent;
     b.x = x*sx;
     b.y = y*sy;
     b.z = z*sz;
     b.x_abs = b.x;
     b.y_abs = b.y;
     b.z_abs = b.z;

     // add bone
     bonemap.insert(boneitem(i, b));
    }

 for(bone_iterator i = bonemap.begin(); i != bonemap.end(); i++)
    {
     bone_iterator iter = bonemap.find(i->second.parent);
     if(iter != bonemap.end()) {
        i->second.x_abs += iter->second.x_abs;
        i->second.y_abs += iter->second.y_abs;
        i->second.z_abs += iter->second.z_abs;
       }
     ofile << "v " << i->second.x_abs << " " << i->second.y_abs << " " << i->second.z_abs << endl;
    }

 ifstream chrfile("nina1u.chr", ios::binary);
 chrfile.seekg(0x14BC);
 size_t n_bones = 106;
 float x = 0;
 float y = 0;
 float z = 0;
 for(size_t i = 0; i < n_bones; i++) {
     float m[16];
     m[ 0] = LE_read_float32(chrfile);
     m[ 1] = LE_read_float32(chrfile);
     m[ 2] = LE_read_float32(chrfile);
     m[ 3] = LE_read_float32(chrfile);
     m[ 4] = LE_read_float32(chrfile);
     m[ 5] = LE_read_float32(chrfile);
     m[ 6] = LE_read_float32(chrfile);
     m[ 7] = LE_read_float32(chrfile);
     m[ 8] = LE_read_float32(chrfile);
     m[ 9] = LE_read_float32(chrfile);
     m[10] = LE_read_float32(chrfile);
     m[11] = LE_read_float32(chrfile);
     m[12] = LE_read_float32(chrfile);
     m[13] = LE_read_float32(chrfile);
     m[14] = LE_read_float32(chrfile);
     m[15] = LE_read_float32(chrfile);
     x = m[12];
     y = m[13];
     z = m[14];

     // cout << m[ 0] << " " << m[ 1] << " " << m[ 2] << " " << m[ 3] << endl;
     // cout << m[ 4] << " " << m[ 5] << " " << m[ 6] << " " << m[ 7] << endl;
     // cout << m[ 8] << " " << m[ 9] << " " << m[10] << " " << m[11] << endl;
     // cout << m[12] << " " << m[13] << " " << m[14] << " " << m[15] << endl;
     // cout << endl;

     //ofile << "v " << x << " " << y << " " << z << endl;
    }
*/

 return 0;
}

INT_PTR CALLBACK RipperProc(HWND dialog, UINT message, WPARAM wparam, LPARAM lparam)
{
 switch(message) {
   case(WM_INITDIALOG) :
       {
        HWND system = GetDlgItem(dialog, IDC_SYSTEM);
        if(!system) {
           MessageBox(dialog, TEXT("Dialog control missing."), TEXT("Error"), MB_ICONSTOP);
           EndDialog(dialog, IDCANCEL);
          }
        SendMessage(system, CB_ADDSTRING, 0, (LPARAM)TEXT("Playstation 3"));
        SendMessage(system, CB_ADDSTRING, 0, (LPARAM)TEXT("PC"));
        SendMessage(system, CB_ADDSTRING, 0, (LPARAM)TEXT("XBox 360"));
        SendMessage(system, CB_SETCURSEL, (WPARAM)0, (LPARAM)0);
        return TRUE;
       }
   case(WM_COMMAND) : {
        int cmd = LOWORD(wparam);
        if(cmd == IDOK) {
           EndDialog(dialog, IDOK);
           return TRUE;
          }
        if(cmd == IDCANCEL) {
           EndDialog(dialog, IDCANCEL);
           return TRUE;
          }
        return FALSE;
       }
  }
 return FALSE;
}

bool is_face_flipped(const float* v1, const float* v2, const float* v3, const float* n1, const float* n2, const float* n3)
{
 // compute a
 float a[3] = {
  v2[0] - v1[0],
  v2[1] - v1[1],
  v2[2] - v1[2],
 };
 float da = std::sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]);
 a[0] /= da;
 a[1] /= da;
 a[2] /= da;

 // compute b
 float b[3] = {
  v3[0] - v1[0],
  v3[1] - v1[1],
  v3[2] - v1[2],
 };
 float db = std::sqrt(b[0]*b[0] + b[1]*b[1] + b[2]*b[2]);
 b[0] /= db;
 b[1] /= db;
 b[2] /= db;

 // compute c
 float c[3] = {
  a[1]*b[2] - a[2]*b[1],
  a[2]*b[0] - a[0]*b[2],
  a[0]*b[1] - a[1]*b[0],
 };
 float dc = std::sqrt(c[0]*c[0] + c[1]*c[1] + c[2]*c[2]);
 c[0] /= dc;
 c[1] /= dc;
 c[2] /= dc;

 // compute average of vertex normals
 float q[3] = {
  (n1[0] + n2[0] + n3[0])/3.0f,
  (n1[1] + n2[1] + n3[1])/3.0f,
  (n1[2] + n2[2] + n3[2])/3.0f,
 };
 float dq = std::sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2]);
 q[0] /= dq;
 q[1] /= dq;
 q[2] /= dq;

 // now compute angle between two normals
 float angle = std::acos((c[0]*q[0] + c[1]*q[1] + c[2]*q[2]));
 angle = angle*(180.0f/3.1415f);
 return (angle > 90.0f);
}