/***************************************************************************

  deletemap.c

  (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2, or (at your option)
  any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  MA 02110-1301, USA.

***************************************************************************/

#define __DELETEMAP_C

#include "gambas.h"
#include "main.h"
#include "deletemap.h"

//#define DEBUG_ME

typedef
  struct _DELETE_SLOT {
    struct _DELETE_SLOT *prev;
    struct _DELETE_SLOT *next;
    int start;
    int length;
    }
  DELETE_SLOT;

int DELETE_MAP_virtual_to_real(DELETE_MAP *dmap, int vpos)
{
  DELETE_SLOT *slot = (DELETE_SLOT *)dmap;
  int rpos = vpos;
  
  while (slot)
  {
    if (rpos < slot->start)
      break;
    rpos += slot->length;
    slot = slot->next;
  }
  
  #ifdef DEBUG_ME
  printf("DELETE_MAP_virtual_to_real: %ld => %ld\n", vpos, rpos);
  #endif
  
  return rpos;
}

int DELETE_MAP_real_to_virtual(DELETE_MAP *dmap, int rpos)
{
  DELETE_SLOT *slot = (DELETE_SLOT *)dmap;
  int vpos = rpos;
  
  while (slot)
  {
    if (rpos < slot->start)
      break;
    if (rpos < (slot->start + slot->length))
      return (-1);
    vpos -= slot->length;
    slot = slot->next;
  }
  
  #ifdef DEBUG_ME
  printf("DELETE_MAP_real_to_virtual: %ld => %ld\n", rpos, vpos);
  #endif
  
  return vpos;
}

#ifdef DEBUG_ME
static void dump(DELETE_MAP *dmap)
{
  DELETE_SLOT *slot = (DELETE_SLOT *)dmap;
  
  printf("dumping map %p\n", dmap);
  while (slot)
  {
    printf("[ %ld %ld ]\n", slot->start, slot->length);
    slot = slot->next;
  }
  printf("\n");
}
#endif

static void create_slot(DELETE_SLOT **pslot, int pos, DELETE_SLOT *before, DELETE_SLOT *after)
{
  GB.Alloc(POINTER(pslot), sizeof(DELETE_SLOT));
  (*pslot)->prev = before;
  (*pslot)->next = after;
  (*pslot)->start = pos;
  (*pslot)->length = 1;
  
  if (before)
    before->next = *pslot;
  if (after)
    after->prev = *pslot;
}

static DELETE_SLOT *delete_slot(DELETE_SLOT *slot)
{
  DELETE_SLOT *nslot;
  
  nslot = slot->next;
  
  if (slot->prev)
    slot->prev->next = slot->next;
  if (slot->next)
    slot->next->prev = slot->prev;
    
  GB.Free(POINTER(&slot));
  
  return nslot;
}

void DELETE_MAP_add(DELETE_MAP **dmap, int vpos)
{
  DELETE_SLOT *slot;
  DELETE_SLOT *nslot;
  DELETE_SLOT *bslot = NULL;
  int rpos;
  
  if (vpos < 0)
    return;
  
  rpos = DELETE_MAP_virtual_to_real(*dmap, vpos);
  
  for (slot = (DELETE_SLOT *)*dmap; slot; slot = slot->next)
  {
    if (rpos < slot->start)
      break;
    bslot = slot;
  }
  
  create_slot(&nslot, rpos, bslot, slot);
  if ((DELETE_SLOT *)*dmap == slot)
    *dmap = (DELETE_MAP *)nslot;
  
  slot = nslot;
  if (slot->prev)
    slot = slot->prev;
    
  while (slot->next)
  {
    if ((slot->start + slot->length) == (slot->next->start))
    {
      nslot = slot->next;
      slot->length += nslot->length;
      delete_slot(nslot);
    }
    else
      slot = slot->next;
  }
  
  #ifdef DEBUG_ME
  printf("DELETE_MAP_add: %ld\n", vpos);
  dump(*dmap);
  #endif
}

void DELETE_MAP_free(DELETE_MAP **dmap)
{
  DELETE_SLOT *slot;
  
  slot = (DELETE_SLOT *)*dmap;
  while (slot)
    slot = delete_slot(slot);
    
  *dmap = NULL;
}

