#include "cell.h"

cell * cell_alloc (unsigned int m, unsigned int n)
{
  unsigned int i, j;
  cell * pc = (cell*) malloc (sizeof (cell));
  (* pc).el = (element***) malloc (sizeof (element**) * m);
  for (i = 0; i < m; i++) {
    (* pc).el[i] = malloc (sizeof (element *) * n);
    for (j = 0; j < n; j++) {
      (* pc).el[i][j] = element_alloc ();
    }
  }
  (* pc).m = i;
  (* pc).n = j;
  return pc;
}


void cell_throw_away (cell * pc)
{
  //unsigned int i, j;
  //for (i = 0; i < cell_size_m (pc); i++) {
    //for (j = 0; j < cell_size_n(pc); j++) {
      //element_throw_away((* pc).el[i][j]);
    //}
  //}
  free (pc);
}

void cell_free (cell * pc)
{
  unsigned int i, j;
  for (i = 0; i < cell_size_m (pc); i++) {
    for (j = 0; j < cell_size_n(pc); j++) {
      element_free ((* pc).el[i][j]);
    }
  }
  free (pc);
}

// Uvolni z pameti element na I, J prvku z PC.
void cell_free_element (cell *pc, unsigned int i, unsigned int j)
{
  #ifdef CELL_DEBUG
  if ((i<cell_size_m(pc))&(j<cell_size_n(pc))) {
  #endif
    element_free((* pc).el[i][j]);
  #ifdef CELL_DEBUG
  } else {
    printf("%s:%i: Nepodarilo se smazat prvek C[%i][%i], protoze index je mimo hranice struktury o rozmerech %i x %i.\n", __FILE__, __LINE__, i, j, cell_size_m(pc), cell_size_n(pc));
  }
  #endif
}

cell * cell_clone (cell * pc)
{
  unsigned int i, j;
  cell * clone = cell_alloc((*pc).m, (*pc).n);
  for(i = 0; i<(*pc).m; i++) {
    for(j = 0; j<(*pc).n; j++) {
      cell_set(clone, i, j, element_clone(cell_get(pc, i, j)));
    }
  }
  return clone;
}

void cell_set (cell * pc, unsigned int i, unsigned int j, element * el)
{
#ifdef CELL_DEBUG
  if ((i<cell_size_m(pc))&(j<cell_size_n(pc))) {
#endif
  (* pc).el[i][j] = el;
#ifdef CELL_DEBUG
  } else {
    printf("%s:%i: Index mimo hranice struktury C[%i][%i] o rozmerech %i x %i.\n", __FILE__, __LINE__, i, j, cell_size_m(pc), cell_size_n(pc));
  }
#endif
}


element * cell_get (cell * pc, unsigned int i, unsigned int j)
{
#ifdef CELL_DEBUG
  if ((i<cell_size_m(pc))&(j<cell_size_n(pc))) {
#endif
  return (* pc).el[i][j];
#ifdef CELL_DEBUG
  } else {
    printf("%s:%i: Index mimo hranice struktury C[%i][%i] o rozmerech %i x %i.\n", __FILE__, __LINE__, i, j, cell_size_m(pc), cell_size_n(pc));
  }
#endif
}


unsigned int cell_size_m (cell * pc)
{
  return (* pc).m;
}


unsigned int cell_size_n (cell * pc)
{
  return (* pc).n;
}


cell *cell_cat (cell *A, cell *B, unsigned int how)
{
  unsigned int ma, na, mb, nb, m_new, n_new, i, j;
  cell *NEW;
  // Rozmery
  ma = cell_size_m(A);
  na = cell_size_n(A);
  mb = cell_size_m(B);
  nb = cell_size_n(B);
  
  if (how==CELL_CAT_HORIZONTALLY) {
    #ifdef CELL_DEBUG
    if (ma==mb) {
    #endif
      // Nove rozmery struktury
      m_new = ma;
      n_new = na+nb;  
      // Alokace nove struktury.
      NEW = cell_alloc(m_new, n_new);

      for (i = 0; i<m_new; i++) {
        
        // Naplneni nove struktury daty ze struktury A
        for (j = 0; j<na; j++) {
          cell_set(NEW, i, j, element_clone(cell_get(A, i, j)));
        }
        
        // Naplneni nove struktury daty ze struktury B
        for (j = na; j<n_new; j++) {
          cell_set(NEW, i, j, element_clone(cell_get(B, i, j-na)));
        }
      }
      
      return NEW;

    #ifdef CELL_DEBUG
    } else {
      printf("%s:%i: Struktury nelze hotizontalne spojit protoze ma kazda jiny pocet radku (MA = %i a MB = %i).\n", __FILE__, __LINE__, ma, mb);
      return NULL;
    }
    #endif
  }

  if (how==CELL_CAT_VERTICALLY) {
    #ifdef CELL_DEBUG
    if (na==nb) {
    #endif
      // Nove rozmery struktury
      m_new = ma+mb;
      n_new = na;
      // Alokace nove struktury.
      NEW = cell_alloc(m_new, n_new);

      for (j = 0; j<n_new; j++) {
        // Naplneni nove struktury daty ze struktury A
        for (i = 0; i<ma; i++) {
          cell_set(NEW, i, j, element_clone(cell_get(A, i, j)));
        }

        // Naplneni nove struktury daty ze struktury B
        for (i = ma; i<m_new; i++) {
          cell_set(NEW, i, j, element_clone(cell_get(B, i-ma, j)));
        }
      }

      return NEW;

    #ifdef CELL_DEBUG
    } else {
      printf("%s:%i: Struktury nelze vertikalne spojit protoze ma kazda jiny pocet sloupu (NA = %i a NB = %i).\n", __FILE__, __LINE__, na, nb);
      return NULL;
    }
  #endif
  }
  #ifdef CELL_DEBUG
  printf("%s:%i: Neznama hodnota '%i' vstupni promene HOW\n", __FILE__, __LINE__, how);
  #endif
  return NULL;
}


cell *cell_cat_list(list * L, unsigned int how)
{
  unsigned int I, J, i, j, ma, na, mb, nb, M, N;
  cell *A, *B, *OUT;

  if (list_get_size(L)==0) {
    #ifdef CELL_DEBUG
    printf("%s:%i: Seznam je prazdny.\n", __FILE__, __LINE__);
    #endif
    return NULL;
  }

  if (how==CELL_CAT_HORIZONTALLY) {
    
    N = 0;  // inicializace noveho poctu sloupcu

    // Zjisteni rozmeru prvniho prvku
    list_first(L);
    A = element_get_cell(list_get_curent(L));
    ma = cell_size_m(A);
    N = cell_size_n(A);

    // Test jestli struktury v seznamu maji stejny pocet radku.
    while (list_next(L)) {
      // Zjisteni rozmeru aktualniho prvku.
      B = element_get_cell(list_get_curent(L));
      mb = cell_size_m(B);
      nb = cell_size_n(B);
      if (ma!=mb) {
        #ifdef CELL_DEBUG
        printf("%s:%i: Struktury nelze spojit horizontalne, protoze nemaji stejny pocet radku.\n", __FILE__, __LINE__);
        #endif
        return NULL;
      }

      // Ulozeni rozmeru tohoto prvku do dalsiho cyklu
      ma = mb;
      na = nb;
      // Inkrementace poctu sloupcu.
      N += nb;
      
    }

    M = ma;
    //printf("%i x %i\n", M, N);
    // Alokace vystupni struktury.
    OUT = cell_alloc(M, N);

    // naplneni struktury OUT daty.
    J = 0;
    N = 0;
    list_first(L);
    do {
      A = element_get_cell(list_get_curent(L));
      N += cell_size_n(A);
      j = 0;
      // bude se nastavovat J-ty sloupec
      while (J<N) {
        
        // Bude se nastavovat I-ty radek V j-tem sloupci.
        for (i = 0; i<M; i++) {
          cell_set(OUT, i, J, element_clone(cell_get(A, i, j)));
        }
        j++;
        J++;
      }
    } while (list_next(L));

    return OUT;

  }

  if (how==CELL_CAT_VERTICALLY) {

    M = 0;  // inicializace noveho poctu radku

    // Zjisteni rozmeru prvniho prvku
    list_first(L);
    A = element_get_cell(list_get_curent(L));
    M = cell_size_m(A);
    na = cell_size_n(A);

    // Test jestli struktury v seznamu maji stejny pocet radku.
    while (list_next(L)) {
      // Zjisteni rozmeru aktualniho prvku.
      B = element_get_cell(list_get_curent(L));
      mb = cell_size_m(B);
      nb = cell_size_n(B);
      if (na!=nb) {
        #ifdef CELL_DEBUG
        printf("%s:%i: Struktury nelze spojit horizontalne, protoze nemaji stejny pocet radku.\n", __FILE__, __LINE__);
        #endif
        return NULL;
      }

      // Ulozeni rozmeru tohoto prvku do dalsiho cyklu
      ma = mb;
      na = nb;
      // Inkrementace poctu radku.
      M += mb;

    }

    N = na;

    // Alokace vystupni struktury.
    OUT = cell_alloc(M, N);

    // naplneni struktury OUT daty.
    I = 0;
    M = 0;
    list_first(L);
    do {
      A = element_get_cell(list_get_curent(L));
      M += cell_size_m(A);
      i = 0;
      // bude se nastavovat I-ty radek
      while (I<M) {
        
        // Bude se nastavovat J-ty sloupec V j-tem sloupci.
        for (j = 0; j<N; j++) {
          cell_set(OUT, I, j, element_clone(cell_get(A, i, j)));
        }
        i++;
        I++;
      }
    } while (list_next(L));

    //cell_print_info(OUT);

    return OUT;

  }

}


void cell_print_info (cell * pc)
{
  cell_print_info_indent(pc, 0);
}


void cell_print_info_indent (cell * pc, unsigned int indent)
{
  unsigned int i, j, k, new_indent;
  new_indent = indent+1;
  for (i = 0; i < cell_size_m (pc); i++) {
    for (j = 0; j < cell_size_n (pc); j++) {
      for (k = 0; k < indent; k++) {
        printf("  ");
      }
      printf ("{%i,%i}\n", i, j);
      element_print_info_indent (cell_get (pc, i, j), new_indent);
    }
  }
}

void cell_print_indent (cell * pc, unsigned int indent)
{
  unsigned int i, j, k, new_indent;
  new_indent = indent+1;
  for (i = 0; i < cell_size_m (pc); i++) {
    for (j = 0; j < cell_size_n (pc); j++) {
      for (k = 0; k < indent; k++) {
        printf("  ");
      }
      printf ("{%i,%i}\n", i, j);
      element_print_indent (cell_get (pc, i, j), new_indent);
    }
  }
}

void cell_print (cell *pc)
{
  cell_print_indent(pc, 0);
}


// Vrati 1 pokud struktura neni prazdna, 0 pokud je prazdna (neobsahuje zadne prvky).
int cell_not_null (cell *pc)
{
  if ((pc->m!=0)&(pc->n!=0)) {
    return 1;
  }
  return 0;
}