/* Copyright (c) 2008, 2009
 *      Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
 *      Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
 *      Micah Cowan (micah@cowan.name)
 *      Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
 * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
 *      Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
 *      Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
 * Copyright (c) 1987 Oliver Laumann
 *
 * 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 3, 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 (see the file COPYING); if not, see
 * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 *
 ****************************************************************
 */

#include "config.h"
#include "screen.h"
#include "extern.h"
#include "layout.h"

extern struct display *display;
extern int captionalways;

struct layout *layouts;
struct layout *laytab[MAXLAY];
struct layout *layout_last, layout_last_marker;
struct layout *layout_attach = &layout_last_marker;

void FreeLayoutCv(struct canvas *cv)
{
  struct canvas *cnext, *c = cv;
  for (; cv; cv = cnext) {
    if (cv->c_slperp) {
      FreeLayoutCv(cv->c_slperp);
      free(cv->c_slperp);
      cv->c_slperp = 0;
    }
    cnext = cv->c_slnext;
    cv->c_slnext = 0;
    if (cv != c)
      free(cv);
  }
}

struct layout *CreateLayout(char *title, int startat)
{
  struct layout *lay, **pl;
  int i;

  if (startat >= MAXLAY || startat < 0)
    startat = 0;
  for (i = startat; ;) {
    if (!laytab[i])
      break;
    if (++i == MAXLAY)
      i = 0;
    if (i == startat) {
      Msg(0, "No more layouts\n");
      return 0;
    }
  }
  lay = (struct layout *)calloc(1, sizeof(*lay));
  lay->lay_title = SaveStr(title);
  lay->lay_autosave = 1;
  lay->lay_number = i;
  laytab[i] = lay;
  lay->lay_next = 0;

  pl = &layouts;
  while (*pl)
    pl = &(*pl)->lay_next;
  *pl = lay;
  return lay;
}

void SaveLayout(char *name, struct canvas *cv)
{
  struct layout *lay;
  struct canvas *fcv;
  for (lay = layouts; lay; lay = lay->lay_next)
    if (!strcmp(lay->lay_title, name))
      break;
  if (lay)
    FreeLayoutCv(&lay->lay_canvas);
  else
    lay = CreateLayout(name, 0);
  if (!lay)
    return;
  fcv = D_forecv;
  DupLayoutCv(cv, &lay->lay_canvas, 1);
  lay->lay_forecv = D_forecv;
  D_forecv = fcv;
  D_layout = lay;
}

void AutosaveLayout(struct layout *lay)
{
  struct canvas *fcv;
  if (!lay || !lay->lay_autosave)
    return;
  FreeLayoutCv(&lay->lay_canvas);
  fcv = D_forecv;
  DupLayoutCv(&D_canvas, &lay->lay_canvas, 1);
  lay->lay_forecv = D_forecv;
  D_forecv = fcv;
}

struct layout *FindLayout(char *name)
{
  struct layout *lay;
  char *s;
  int i;
  for (i = 0, s = name; *s >= '0' && *s <= '9'; s++)
    i = i * 10 + (*s - '0');
  if (!*s && s != name && i >= 0 && i < MAXLAY)
    return laytab[i];
  for (lay = layouts; lay; lay = lay->lay_next)
    if (!strcmp(lay->lay_title, name))
      break;
  return lay;
}

void LoadLayout(struct layout *lay, struct canvas *cv)
{
  AutosaveLayout(D_layout);
  if (!lay) {
    while (D_canvas.c_slperp)
      FreeCanvas(D_canvas.c_slperp);
    MakeDefaultCanvas();
    SetCanvasWindow(D_forecv, 0);
    D_layout = 0;
    return;
  }
  while (D_canvas.c_slperp)
    FreeCanvas(D_canvas.c_slperp);
  D_cvlist = 0;
  D_forecv = lay->lay_forecv;
  if (!D_forecv)
    MakeDefaultCanvas();
  DupLayoutCv(&lay->lay_canvas, &D_canvas, 0);
  D_canvas.c_ys = (D_has_hstatus == HSTATUS_FIRSTLINE);
  D_canvas.c_ye = D_height - 1 - ((D_canvas.c_slperp && D_canvas.c_slperp->c_slnext) ||
                  captionalways) - (D_has_hstatus == HSTATUS_LASTLINE);
  ResizeCanvas(&D_canvas);
  RecreateCanvasChain();
  RethinkDisplayViewports();
  PutWindowCv(&D_canvas);
  ResizeLayersToCanvases();
  D_layout = lay;
}

void NewLayout(char *title, int startat)
{
  struct layout *lay;
  struct canvas *fcv;

  lay = CreateLayout(title, startat);
  if (!lay)
    return;

  if (display) {
    LoadLayout(0, &D_canvas);
    fcv = D_forecv;
    DupLayoutCv(&D_canvas, &lay->lay_canvas, 1);
    lay->lay_forecv = D_forecv;
    D_forecv = fcv;
    D_layout = lay;
  }
  else
    layout_attach = lay;

  lay->lay_autosave = 1;
}


static char *AddLayoutsInfo(char *buf, int len, int where)
{
  char *s, *ss, *t;
  struct layout *p, **pp;
  int l;

  s = ss = buf;
  for (pp = laytab; pp < laytab + MAXLAY; pp++) {
    if (pp - laytab == where && ss == buf)
      ss = s;
    if ((p = *pp) == 0)
      continue;
    t = p->lay_title;
    l = strlen(t);
    if (l > 20)
      l = 20;
    if (s - buf + l > len - 24)
      break;
    if (s > buf) {
      *s++ = ' ';
      *s++ = ' ';
    }
    sprintf(s, "%d", p->lay_number);
    if (p->lay_number == where)
	ss = s;
    s += strlen(s);
    if (display && p == D_layout)
      *s++ = '*';
    *s++ = ' ';
    strncpy(s, t, l);
    s += l;
  }
  *s = 0;
  return ss;
}

void ShowLayouts(int where)
{
  char buf[1024];
  char *s, *ss;

  if (!display)
    return;
  if (!layouts) {
    Msg(0, "No layouts defined\n");
    return;
  }
  if (where == -1 && D_layout)
    where = D_layout->lay_number;
  ss = AddLayoutsInfo(buf, sizeof(buf), where);
  s = buf + strlen(buf);
  if (ss - buf > D_width / 2) {
    ss -= D_width / 2;
    if (s - ss < D_width) {
      ss = s - D_width;
      if (ss < buf)
        ss = buf;
    }
  }
  else
    ss = buf;
  Msg(0, "%s", ss);
}

void RemoveLayout(struct layout *lay)
{
  struct layout **layp = &layouts;

  for (; *layp; layp = &(*layp)->lay_next) {
    if (*layp == lay) {
      *layp = lay->lay_next;
      break;
    }
  }
  laytab[lay->lay_number] = (struct layout *)0;

  if (display && D_layout == lay)
    D_layout = (struct layout *)0;

  FreeLayoutCv(&lay->lay_canvas);

  if (lay->lay_title)
    free(lay->lay_title);
  free(lay);

  if (layouts)
    LoadLayout((display && D_layout) ? D_layout : *layp ? *layp : layouts,
	display ? &D_canvas : (struct canvas *)0);
  Activate(0);
}

void UpdateLayoutCanvas(struct canvas *cv, struct win * wi)
{
  for (; cv; cv = cv->c_slnext) {
    if (cv->c_layer && Layer2Window(cv->c_layer) == wi) {
      /* A simplistic version of SetCanvasWindow(cv, 0) */
      struct layer *l = cv->c_layer;
      cv->c_layer = 0;
      if (l->l_cvlist == 0 && (wi == 0 || l != wi->w_savelayer))
        KillLayerChain(l);

      l = &cv->c_blank;
      l->l_data = 0;
      if (l->l_cvlist != cv) {
        cv->c_lnext = l->l_cvlist;
        l->l_cvlist = cv;
      }
      cv->c_layer = l;
      /* Do not end here. Multiple canvases can have the same window */
    }

    if (cv->c_slperp)
      UpdateLayoutCanvas(cv->c_slperp, wi);
  }
}


static void dump_canvas(struct canvas *cv, FILE *file)
{
  struct canvas *c;
  for (c = cv->c_slperp; c && c->c_slnext; c = c->c_slnext)
    fprintf(file, "split%s\n", c->c_slorient == SLICE_HORI ? " -v" : "");
    
  for (c = cv->c_slperp; c; c = c->c_slnext) {
    if (c->c_slperp)
      dump_canvas(c, file);
    else
      fprintf(file, "focus\n");
  }
}

int LayoutDumpCanvas(struct canvas *cv, char *filename)
{
  FILE *file = secfopen(filename, "a");
  if (!file)
    return 0;
  dump_canvas(cv, file);
  fclose(file);
  return 1;
}

void RenameLayout(struct layout *layout, const char *name)
{
  free(layout->lay_title);
  layout->lay_title = SaveStr(name);
}

int RenumberLayout(struct layout *layout, int number)
{
  int old;
  struct layout *lay;
  old = layout->lay_number;
  if (number < 0 || number >= MAXLAY)
    return 0;
  lay = laytab[number];
  laytab[number] = layout;
  layout->lay_number = number;
  laytab[old] = lay;
  if (lay)
    lay->lay_number = old;
  return 1;
}
