/**
 * Copyright (c) 2021 Dominic Masters
 * 
 * This software is released under the MIT License.
 * https://opensource.org/licenses/MIT
 */

#include "../utils/common.h"
#include "../utils/file.h"
#include "../utils/image.h"
#include "../utils/xml.h"

int main(int argc, char *argv[]) {
  FILE *file;
  char *in, *out;
  char bufferA[FILE_CHILD_NAME_MAX * FILE_CHILD_COUNT_MAX];
  char bufferB[FILENAME_MAX];
  char directory[FILENAME_MAX];
  uint8_t i, *pixels, *data;
  int32_t fullWidth, fullHeight, size;
  int32_t j, baseWidth, baseHeight, childrenCount, l, x, y, w, h, px, py, iw, ih;
  xml_t node, *base, *child;
  uint8_t childrenTypes[FILE_CHILD_COUNT_MAX];
  char *children[FILE_CHILD_COUNT_MAX];
  
  if(argc != 3) {
    printf("Invalid number of arguments\n");
    return 1;
  }

  // Set up strings
  in = argv[1];
  out = argv[2];

  // Normalize slashes
  fileNormalizeSlashes(in);
  fileNormalizeSlashes(out);

  // Check the output doesn't already exist
  file = fopen(out, "rb");
  if(file != NULL) {
    fclose(file);
    return 0;
  }

  // Open XML file
  file = fopen(in, "rb");
  if(file == NULL) {
    printf("Failed to open file!\n");
    return 1;
  }
  
  // Bufer XML data
  assetReadString(file, bufferA);
  fclose(file);
  xmlLoad(&node, bufferA);

  // Begin parsing
  if(strcmp(node.node, "vncharacter") != 0) {
    printf("Invalid character XML!\n");
    xmlDispose(&node);
    return 1;
  }

  // Find base
  base = NULL;
  for(i = 0; i < node.childrenCount; i++) {
    if(strcmp(node.children[i].node, "base") != 0) continue;
    base = node.children + i;
    break;
  }

  if(base == NULL) {
    printf("XML is missing base layer!\n");
    xmlDispose(&node);
    return 1;
  }

  // Prepare to load base info
  fileGetDirectory(in, bufferA);
  sprintf(directory, "%s%c%s", 
    bufferA,
    FILE_PATH_SEP,
    node.attributeDatas[xmlGetAttributeByName(&node, "context")]
  );

  sprintf(bufferA, "%s%c%s",
    directory,
    FILE_PATH_SEP,
    base->attributeDatas[xmlGetAttributeByName(base, "file")]
  );
  printf("Reading texture info for %s\n", bufferA);

  file = fopen(bufferA, "rb");
  if(file == NULL) {
    printf("Failed to load base texture file %s!\n", bufferA);
    xmlDispose(&node);
    return 1;
  }

  // Read base info.
  if(!stbi_info_from_file(file, &baseWidth, &baseHeight, NULL)) {
    printf("Failed to read base texture %s!\n", bufferA);
    xmlDispose(&node);
    fclose(file);
    return 1;
  }


  
  // Read in the information for each layer.
  fullWidth = 0;
  fullHeight = 0;

  for(i = 0; i < node.childrenCount; i++) {
    child = node.children + i;
    if(strcmp(child->node, "layer") != 0) continue;

    // Get the full path of the directory where the layers' images reside
    sprintf(bufferB, "%s%c%s",
      directory,
      FILE_PATH_SEP,
      child->attributeDatas[xmlGetAttributeByName(child, "directory")]
    );

    // Scan the directory.
    if(!fileListChildren(
      bufferB, bufferA, &childrenCount, childrenTypes, children
    )) {
      printf("Failed to scandir!\n");
      xmlDispose(&node);
      fclose(file);
      return 1;
    }
    
    if(childrenCount == 0) continue;

    // Update sizes
    size = childrenCount * atoi(
      child->attributeDatas[xmlGetAttributeByName(child, "width")]
    );
    if(size > fullWidth) fullWidth = size;
    fullHeight += atoi(
      child->attributeDatas[xmlGetAttributeByName(child, "height")]
    );
  }

  // Update final full sizes
  fullWidth += baseWidth;
  fullHeight = fullHeight > baseHeight ? fullHeight : baseHeight;
  l = STBI_rgb_alpha;

  // Create output data
  pixels = calloc(fullWidth * fullHeight * l, sizeof(uint8_t));
  if(pixels == NULL) {
    xmlDispose(&node);
    fclose(file);
    printf("Failed to create memory for pixels!\n");
    return 1;
  }

  // Read in base data
  data = stbi_load_from_file(file, &baseWidth, &baseHeight, NULL, l);
  imageCopy(
    data, baseWidth, baseHeight,
    pixels, fullWidth, fullHeight,
    -1, -1, -1, -1,
    -1, -1,
    l
  );
  stbi_image_free(data);
  fclose(file);

  // Now read in each layer
  x = 0;
  y = 0;
  py = 0;

  for(i = 0; i < node.childrenCount; i++) {
    child = node.children + i;
    if(strcmp(child->node, "layer") != 0) continue;

    // Get the full path of the directory where the layers' images reside
    sprintf(bufferB, "%s%c%s",
      directory,
      FILE_PATH_SEP,
      child->attributeDatas[xmlGetAttributeByName(child, "directory")]
    );
    
    // Scan the directory.
    if(!fileListChildren(
      bufferB, bufferA, &childrenCount, childrenTypes, children
    )) {
      printf("Failed to scandir!\n");
      xmlDispose(&node);
      free(pixels);
      return 1;
    }
    if(childrenCount == 0) continue;

    // Read in layer info
    x = atoi(child->attributeDatas[xmlGetAttributeByName(child, "x")]);
    y = atoi(child->attributeDatas[xmlGetAttributeByName(child, "y")]);
    w = atoi(child->attributeDatas[xmlGetAttributeByName(child, "width")]);
    h = atoi(child->attributeDatas[xmlGetAttributeByName(child, "height")]);

    // Reset for the iteration.
    px = baseWidth;
    ih = 0;

    // For each image in the layer...
    for(j = 0; j < childrenCount; j++) {
      // Find the path
      sprintf(bufferB, "%s%c%s%c%s",
        directory,
        FILE_PATH_SEP,
        child->attributeDatas[xmlGetAttributeByName(child, "directory")],
        FILE_PATH_SEP,
        children[j]
      );

      // Open image file
      file = fopen(bufferB, "rb");
      if(file == NULL) {
        printf("Failed to open %s for reading!\n", bufferB);
        xmlDispose(&node);
        free(pixels);
      }
      
      // Copy the cropped area
      data = stbi_load_from_file(file, &iw, &ih, NULL, l);
      imageCopy(
        data, iw, ih,
        pixels, fullWidth, fullHeight,
        x, y, w, h,
        px, py, l
      );

      // Cleanup
      stbi_image_free(data);
      fclose(file);

      // Prep for next image
      px += w;
    }

    // Prepare for next row.
    py += h;
  }

  // Done with the XML!
  xmlDispose(&node);


  // Now write the data!
  fileMkdirp(out);
  stbi_write_png(out,
    fullWidth, fullHeight, l, pixels,
    fullWidth * l
  );

  // Cleanup
  free(pixels);
  return 0;
}