#include "private.h"
#include <Elementary.h>
#include "termpty.h"
#include "backlog.h"


static int ts_comp = 0;
static int ts_uncomp = 0;
static int ts_freeops = 0;
static Eina_List *ptys = NULL;

static int64_t _mem_used = 0;

static void
_accounting_change(int64_t diff)
{
   if (diff > 0)
     {
        diff = ((diff + 16-1) / 16) * 16;
     }
   else
     {
        diff = ((-1 * diff + 16-1) / 16) * -16;
     }
   _mem_used += diff;
}

int64_t
termpty_backlog_memory_get(void)
{
   return _mem_used;
}


void
termpty_save_register(Termpty *ty)
{
   termpty_backlog_lock();
   ptys = eina_list_append(ptys, ty);
   termpty_backlog_unlock();
}

void
termpty_save_unregister(Termpty *ty)
{
   termpty_backlog_lock();
   ptys = eina_list_remove(ptys, ty);
   termpty_backlog_unlock();
}

Termsave *
termpty_save_extract(Termsave *ts)
{
   if (!ts) return NULL;
   return ts;
}

Termsave *
termpty_save_new(Termpty *ty, Termsave *ts, int w)
{
   termpty_save_free(ty, ts);

   Termcell *cells = calloc(1, w * sizeof(Termcell));
   if (!cells ) return NULL;
   ts->cells = cells;
   ts->w = w;
   _accounting_change(w * sizeof(Termcell));
   return ts;
}

Termsave *
termpty_save_expand(Termpty *ty, Termsave *ts, Termcell *cells, size_t delta)
{
   Termcell *newcells;

   newcells = realloc(ts->cells, (ts->w + delta) * sizeof(Termcell));
   if (!newcells)
     return NULL;

   memset(newcells + ts->w,
          0, delta * sizeof(Termcell));
   TERMPTY_CELL_COPY(ty, cells, &newcells[ts->w], (int)delta);

   _accounting_change((-1) * (int64_t)(ts->w * sizeof(Termcell)));
   ts->w += delta;
   _accounting_change(ts->w * sizeof(Termcell));
   ts->cells = newcells;
   return ts;
}

void
termpty_save_free(Termpty *ty, Termsave *ts)
{
   unsigned int i;
   if (!ts) return;
   if (ts->comp) ts_comp--;
   else ts_uncomp--;
   ts_freeops++;
   for (i = 0; i < ts->w; i++)
     {
        if (EINA_UNLIKELY(ts->cells[i].att.link_id))
          term_link_refcount_dec(ty, ts->cells[i].att.link_id, 1);
     }
   free(ts->cells);
   ts->cells = NULL;
   _accounting_change((-1) * (int64_t)(ts->w * sizeof(Termcell)));
   ts->w = 0;
}

void
termpty_backlog_lock(void)
{
}

void
termpty_backlog_unlock(void)
{
}

void
termpty_backlog_free(Termpty *ty)
{
   size_t i;

   if (!ty || !ty->back)
     return;

   for (i = 0; i < ty->backsize; i++)
     termpty_save_free(ty, &ty->back[i]);
   _accounting_change((-1) * (int64_t)(sizeof(Termsave) * ty->backsize));
   free(ty->back);
   ty->back = NULL;
}

void
termpty_clear_backlog(Termpty *ty)
{
   int backsize;

   ty->backlog_beacon.screen_y = 0;
   ty->backlog_beacon.backlog_y = 0;

   termpty_backlog_lock();
   termpty_backlog_free(ty);
   ty->backpos = 0;
   backsize = ty->backsize;
   ty->backsize = 0;
   termpty_backlog_size_set(ty, backsize);
   termpty_backlog_unlock();
}

ssize_t
termpty_backlog_length(Termpty *ty)
{
   int backlog_y = ty->backlog_beacon.backlog_y;
   int screen_y = ty->backlog_beacon.screen_y;

   if (!ty->backsize)
     return 0;

   for (backlog_y++; backlog_y < (int)ty->backsize; backlog_y++)
     {
        int nb_lines;
        const Termsave *ts;

        ts = BACKLOG_ROW_GET(ty, backlog_y);
        if (!ts->cells)
          goto end;

        nb_lines = (ts->w == 0) ? 1 : (ts->w + ty->w - 1) / ty->w;
        screen_y += nb_lines;
        ty->backlog_beacon.screen_y = screen_y;
        ty->backlog_beacon.backlog_y = backlog_y;
     }
end:
     return ty->backlog_beacon.screen_y;
}


void
termpty_backlog_size_set(Termpty *ty, size_t size)
{
   Termsave *new_back;
   size_t i;

   if (ty->backsize == size)
     return;

   termpty_backlog_lock();

   if (size == 0)
     {
        termpty_backlog_free(ty);
        goto end;
     }
   if (size > ty->backsize)
     {
        new_back = realloc(ty->back, sizeof(Termsave) * size);
        if (!new_back)
          return;
        memset(new_back + ty->backsize, 0,
               sizeof(Termsave) * (size - ty->backsize));
        ty->back = new_back;
     }
   else
     {
        new_back = calloc(1, sizeof(Termsave) * size);
        if (!new_back)
          return;
        for (i = 0; i < size; i++)
          new_back[i] = ty->back[i];
        for (i = size; i < ty->backsize; i++)
          termpty_save_free(ty, &ty->back[i]);
        free(ty->back);
        ty->back = new_back;
     }
   _accounting_change((size - ty->backsize) * (int64_t)sizeof(Termsave));
end:
   ty->backpos = 0;
   ty->backsize = size;
   /* Reset beacon */
   ty->backlog_beacon.screen_y = 0;
   ty->backlog_beacon.backlog_y = 0;

   termpty_backlog_unlock();
}
