/*****************************************************************************
 * This file is part of Kvazaar HEVC encoder.
 *
 * Copyright (c) 2021, Tampere University, ITU/ISO/IEC, project contributors
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * 
 * * Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 * 
 * * Neither the name of the Tampere University or ITU/ISO/IEC nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
 ****************************************************************************/

//Compile with gcc -o generate_tables generate_tables.c
//Run ./generate_tables > ../src/tables.c

#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "../src/global.h"
#include "../src/tables.h"

const uint32_t* _sig_last_scan[3][5];
int8_t _convert_to_bit[LCU_WIDTH + 1];

/**
 * Initialize g_sig_last_scan with scan positions for a transform block of
 * size width x height.
 */
static void init_sig_last_scan(uint32_t *buff_d, uint32_t *buff_h,
                               uint32_t *buff_v,
                               int32_t width, int32_t height)
{
  uint32_t num_scan_pos  = width * width;
  uint32_t next_scan_pos = 0;
  int32_t  xx, yy, x, y;
  uint32_t scan_line;
  uint32_t blk_y, blk_x;
  uint32_t blk;
  uint32_t cnt = 0;

  assert(width == height && width <= 32);

  if (width <= 4) {
    uint32_t *buff_tmp = buff_d;

    for (scan_line = 0; next_scan_pos < num_scan_pos; scan_line++) {
      int    primary_dim  = scan_line;
      int    second_dim  = 0;

      while (primary_dim >= width) {
        second_dim++;
        primary_dim--;
      }

      while (primary_dim >= 0 && second_dim < width) {
        buff_tmp[next_scan_pos] = primary_dim * width + second_dim ;
        next_scan_pos++;
        second_dim++;
        primary_dim--;
      }
    }
  }

  if (width > 4 && width <= 32) {
    uint32_t num_blk_side = width >> 2;
    uint32_t num_blks   = num_blk_side * num_blk_side;
    uint32_t log2_width = g_to_bits[width];

    for (blk = 0; blk < num_blks; blk++) {
      uint32_t init_blk_pos = g_sig_last_scan_cg[log2_width][SCAN_DIAG][blk];
      next_scan_pos   = 0;

      {
        uint32_t offset_y    = init_blk_pos / num_blk_side;
        uint32_t offset_x    = init_blk_pos - offset_y * num_blk_side;
        uint32_t offset_d    = 4 * (offset_x + offset_y * width);
        uint32_t offset_scan = 16 * blk;

        for (scan_line = 0; next_scan_pos < 16; scan_line++) {
          int    primary_dim  = scan_line;
          int    second_dim  = 0;

          //TODO: optimize
          while (primary_dim >= 4) {
            second_dim++;
            primary_dim--;
          }

          while (primary_dim >= 0 && second_dim < 4) {
            buff_d[next_scan_pos + offset_scan] = primary_dim * width +
                                                  second_dim + offset_d;
            next_scan_pos++;
            second_dim++;
            primary_dim--;
          }
        }
      }
    }
  }

  if (width > 2) {
    uint32_t num_blk_side = width >> 2;

    for (blk_y = 0; blk_y < num_blk_side; blk_y++) {
      for (blk_x = 0; blk_x < num_blk_side; blk_x++) {
        uint32_t offset    = blk_y * 4 * width + blk_x * 4;

        for (y = 0; y < 4; y++) {
          for (x = 0; x < 4; x++) {
            buff_h[cnt] = y * width + x + offset;
            cnt ++;
          }
        }
      }
    }

    cnt = 0;

    for (blk_x = 0; blk_x < num_blk_side; blk_x++) {
      for (blk_y = 0; blk_y < num_blk_side; blk_y++) {
        uint32_t offset = blk_y * 4 * width + blk_x * 4;

        for (x = 0; x < 4; x++) {
          for (y = 0; y < 4; y++) {
            buff_v[cnt] = y * width + x + offset;
            cnt ++;
          }
        }
      }
    }
  } else {
    for (yy = 0; yy < height; yy++) {
      for (xx = 0; xx < width; xx++) {
        buff_h[cnt] = yy * width + xx;
        cnt ++;
  }
      }

    cnt = 0;

    for (xx = 0; xx < width; xx++) {
      for (yy = 0; yy < height; yy++) {
        buff_v[cnt] = yy * width + xx;
        cnt ++;
      }
    }
  }
}


void init_tables(void)
{
  int i;
  int c = 0;

  memset( _convert_to_bit,-1, sizeof( _convert_to_bit ) );

  for (i = 4; i < LCU_WIDTH; i *= 2) {
    _convert_to_bit[i] = (int8_t)c;
    c++;
  }

  _convert_to_bit[i] = (int8_t)c;

  c = 2;
  for (i = 0; i < 5; i++) {
    uint32_t *sls0, *sls1, *sls2;
    sls0 = (uint32_t*)malloc(c*c*sizeof(uint32_t));
    sls1 = (uint32_t*)malloc(c*c*sizeof(uint32_t));
    sls2 = (uint32_t*)malloc(c*c*sizeof(uint32_t));

    init_sig_last_scan(sls0, sls1, sls2, c, c);
    
    _sig_last_scan[0][i] = sls0;
    _sig_last_scan[1][i] = sls1;
    _sig_last_scan[2][i] = sls2;
    
    c <<= 1;
  }
}

void free_tables(void)
{
  int i;
  for (i = 0; i < 5; i++) {
    FREE_POINTER(_sig_last_scan[0][i]);
    FREE_POINTER(_sig_last_scan[1][i]);
    FREE_POINTER(_sig_last_scan[2][i]);
  }
}


int main() {
  int i, c, j, h;
  printf("//The file tables.c is automatically generated by generate_tables, do not edit.\n\n");
  printf("#include \"tables.h\"\n\n");
  printf("#if LCU_WIDTH!=%d\n", LCU_WIDTH);
  printf("#error \"LCU_WIDTH!=%d\"\n", LCU_WIDTH);
  printf("#endif\n\n");
  
  init_tables();
  
  printf("const int8_t g_convert_to_bit[LCU_WIDTH + 1] = {");
  for (i=0; i < LCU_WIDTH + 1; ++i) {
    if (i!=LCU_WIDTH) {
      printf("%d, ", _convert_to_bit[i]);
    } else {
      printf("%d", _convert_to_bit[i]);
    }
  }
  printf("};\n\n");
  
  c = 2;
  for (i = 0; i < 5; i++) {
    for (h = 0; h < 3; h++) {
      printf("static const uint32_t g_sig_last_scan_%d_%d[%d] = {",h,i,c*c);
      
      for (j = 0; j < c*c; ++j) {
        if (j!=c*c-1) {
          printf("%u, ", _sig_last_scan[h][i][j]);
        } else {
          printf("%u", _sig_last_scan[h][i][j]);
        }
        if (j % 100 == 99) printf("\n  ");
      }
      printf("};\n");
    }
    printf("\n");
    c <<= 1;
  }
  
  printf("const uint32_t* const g_sig_last_scan[3][5] = {\n");
  for (h = 0; h < 3; h++) {
    printf("  {");
    for (i = 0; i < 5; i++) {
      if (i!=4) {
        printf("g_sig_last_scan_%d_%d, ", h, i);
      } else {
        printf("g_sig_last_scan_%d_%d", h, i);
      }
    }
    if (h<2) {
      printf("},\n");
    } else {
      printf("}\n");
    }
  }
  printf("};\n");
  return 0;
}