/*=========================================================================*/
/* COLORBAR.C - 03/03/96                                                   */
/* Color Bar implementation.                                               */
/*=========================================================================*/

/*- Constantes: -----------------------------------------------------------*/
#define NUMCOLORS 16

/*- Bibliotecas padrao usadas ---------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

/*- Inclusao das bibliotecas IUP e CD: ------------------------------------*/
#include <iup.h>
#include <cd.h>
#include <cd_old.h>
#include <cdiup.h>
#include <iupkey.h>

/*- Declaraccoes e Prototypes: --------------------------------------------*/
#include "cdtest.h"

#undef isdigit
#include <ctype.h>


/*- Globais: --------------------------------------------------------------*/
static struct {
  cdCanvas *bar_canvas;             /* canvas da colorbar */
  cdCanvas *other_canvas;           /* canvas ativo anteriormente */
  int w, h;                         /* dimensoes do canvas */
  int bgr, bgg, bgb;                /* cor de fundo do dialogo pai */
  int bci, fci;                     /* indice das cores correntes */
  long *p_foreground, *p_background;              /* variaveis do  usuario */
  long colors[NUMCOLORS];       /* palheta interna */
  int bounds[2*(NUMCOLORS+1)];      /* fronteiras dos elementos da palheta */ 
} colorbar;                         /* contexto da colorbar */

/*- Dialogo para mudancca da cor da palheta: ------------------------------*/
static struct {
  Ihandle *dialog;
  Ihandle *red;
  Ihandle *green;
  Ihandle *blue;
  Ihandle *alpha;
  Ihandle *bt_ok;
  Ihandle *bt_cancel;
  int to_change;
} color_change;

/*- Macros: ---------------------------------------------------------------*/
#define ignore(_) (void)(_)

/*-------------------------------------------------------------------------*/
/* Filtra inteiros.                                                        */
/*-------------------------------------------------------------------------*/
static int integer(Ihandle *self, int c)
{
  ignore(self);

  if (isdigit(c)) {
    return IUP_DEFAULT;
  }
  else if ((c==K_TAB) || (c==K_CR) || (c==K_LEFT) ||
           (c==K_RIGHT) || (c==K_DEL) || (c==K_BS) || (c==K_sTAB)) {
    return IUP_DEFAULT;
  }
  else {
    return IUP_IGNORE;
  }
}

/*-------------------------------------------------------------------------*/
/* Retorna a cor de indice i.                                              */
/*-------------------------------------------------------------------------*/
static long getcolor(int i)
{
  if (i<=NUMCOLORS) {
    return colorbar.colors[i-1];
  }
  else return 0;
}

/*-------------------------------------------------------------------------*/
/* Mostra a caixa de dialogo Color Change.                                 */
/*-------------------------------------------------------------------------*/
static void color_change_show(int c)
{
  unsigned char r, g, b, a;
  long color;
  char s[4];

  /* mostra o dialogo Color Change */
  IupShow(color_change.dialog);

  /* mostra a cor atual */
  color = getcolor(c);
  cdDecodeColor(color, &r, &g, &b);
  sprintf(s, "%d", r);
  IupSetAttribute(color_change.red, IUP_VALUE, s);
  sprintf(s, "%d", g);
  IupSetAttribute(color_change.green, IUP_VALUE, s);
  sprintf(s, "%d", b);
  IupSetAttribute(color_change.blue, IUP_VALUE, s);

  a = cdDecodeAlpha(color);
  sprintf(s, "%d", a);
  IupSetAttribute(color_change.alpha, IUP_VALUE, s);

  /* salva cor a ser alterada no contexto */
  color_change.to_change = c;
}


/*-------------------------------------------------------------------------*/
/* Cancela a operaccao de mudancca de cor.                                 */
/*-------------------------------------------------------------------------*/
static int color_change_cancel(void)
{
  IupHide(color_change.dialog);

  return IUP_DEFAULT;
}

/*-------------------------------------------------------------------------*/
/* Determina as fronteiras dos elementos da palheta.                       */
/*-------------------------------------------------------------------------*/
static void bounds(int dx, int p, int *x)
{
  int i;                                /* contador dos intervalos */
  int j;                                /* indice do vetor x */
  int k;                                /* inicio do intervalo */
  int e;                                /* erro da aproximacao inteira */
  int _2p;                              /* dobro do numero de intervalos */
  int q;                                /* quociente da divisao inteira */
  int _2r;                              /* dobro do resto da divisao inteira */

  /* inicializa as variaveis */
  k = 0;                                /* inicio do primeiro intervalo */
  j = 0;                                /* indice do vetor x */
  e = 0;                                /* inicializa o erro */
  _2p = p << 1;                         /* dobro do numero de intervalos */
  _2r = (dx % p) << 1;                  /* dobro do resto da divisao inteira */
  q = dx / p;                           /* quociente da divisao inteira */

  /* gera o vetor de intervalos */
  for (i=0; i<p; i++) {                 /* para p intervalos */
    e += _2r;                           /* incrementa o erro */
    x[j++] = k;                         /* inicio do intervalo */
    if (e >= p) {                       /* estourou? */
      e -= _2p;                         /* ajusta o novo limite */
      k += q + 1;                       /* arredonda para cima */
    } 
    else {
      k += q;                           /* arredonda para baixo */
    }
    x[j++] = k - 2;                     /* fim do intervalo */
  }
}

/*-------------------------------------------------------------------------*/
/* Acha a cor onde o mouse foi clicado por busca binaria. Retorna zero se  */ 
/* o click foi fora de qualquer cor.                                       */
/*-------------------------------------------------------------------------*/
static int findwhere(int x, int y, int c1, int c2)
{
  int mid;
  
  /* so pode ser este ou o click foi fora */
  if (c1 == c2) {
    if ((x > colorbar.bounds[c1<<1]) && (x < colorbar.bounds[(c1<<1) + 1])) {
      if ((y > 0) && (y < (colorbar.h-1))) return c1;
      else return 0;
    }
    else return 0;
  }
  
  /* elemento intermediario */
  mid = (c1 + c2)>>1;
  
  /* se o click estah a direita do elemento intermediario */
  if (x > colorbar.bounds[(mid<<1) + 1]) return findwhere(x, y, mid+1, c2);
  
  /* se estah a esquerda do elemento intermediario */
  else if (x < colorbar.bounds[mid<<1]) return findwhere(x, y, c1, mid-1);
  
  /* estah no meio do intermediario e a vertical estah legal, eh esse */
  else if ((y > 0) && (y < (colorbar.h-1))) return mid;

  /* o programa nunca chega aqui, mas o compilador fica feliz */
  return 0;
}

/*-------------------------------------------------------------------------*/
/* Desenha a moldura do elemento da palheta.                               */
/*-------------------------------------------------------------------------*/
static void hollowbox(int xmin, int xmax, int ymin, int ymax)
{
  cdForeground(cdEncodeColor(255, 255, 255));
  cdLine(xmin, ymin, xmax, ymin);
  cdLine(xmax, ymin+1, xmax, ymax);

  cdForeground(cdEncodeColor(102, 102, 102));
  cdLine(xmin, ymin+1, xmin, ymax);
  cdLine(xmin+1, ymax, xmax-1, ymax);
}


/*-------------------------------------------------------------------------*/
/* Muda a cor de indice i e retorna a anterior.                            */
/*-------------------------------------------------------------------------*/
static long changecolor(int i, long color)
{
  long temp;

  if (i<=NUMCOLORS) {
    temp = colorbar.colors[i-1];
    colorbar.colors[i-1] = color;
    return temp;
  }
  else return 0;
}


/*-------------------------------------------------------------------------*/
/* Inicializa o vetor de cores.                                            */
/*-------------------------------------------------------------------------*/
static void resetcolors(void)
{
  colorbar.colors[ 0] = cdEncodeColor(  0,   0,   0);
  colorbar.colors[ 1] = cdEncodeColor(153, 153, 153);
  colorbar.colors[ 2] = cdEncodeColor(178, 178, 178);
  colorbar.colors[ 3] = cdEncodeColor(255, 255, 255);
  colorbar.colors[ 4] = cdEncodeColor(255, 255,   0);
  colorbar.colors[ 5] = cdEncodeColor(255,   0,   0);
  colorbar.colors[ 6] = cdEncodeColor(255,   0, 255);
  colorbar.colors[ 7] = cdEncodeColor(  0, 255, 255);
  colorbar.colors[ 8] = cdEncodeColor(  0,   0, 255);
  colorbar.colors[ 9] = cdEncodeColor(  0, 255,   0);
  colorbar.colors[10] = cdEncodeColor(128, 128,   0);
  colorbar.colors[11] = cdEncodeColor(128,   0,   0);
  colorbar.colors[12] = cdEncodeColor(128,   0, 128);
  colorbar.colors[13] = cdEncodeColor(  0, 128, 128);
  colorbar.colors[14] = cdEncodeColor(  0,   0, 128);
  colorbar.colors[15] = cdEncodeColor(  0, 128,   0);
}

/*-------------------------------------------------------------------------*/
/* Desenha a ColorBar.                                                     */
/*-------------------------------------------------------------------------*/
static int fColorBarRepaint(void)
{
  int i;
  double dt;

  /* salva o canvas ativo no momento */
  colorbar.other_canvas = cdActiveCanvas();

  /* ativa o canvas da colorbar */
  if (cdActivate(colorbar.bar_canvas) == CD_ERROR) {
    printf("Color Bar Error: Unable to activate canvas.");
    return 1;
  }
  cdClear();

  /* desenha as cores da palheta */
  for (i=2; i<=2*NUMCOLORS; i+=2) {
    cdForeground(getcolor(i>>1));
    cdBox(colorbar.bounds[i], colorbar.bounds[i+1], 1, colorbar.h-1);
    hollowbox(colorbar.bounds[i], colorbar.bounds[i+1], 1, colorbar.h-1);
  }

  /* desenha o fedback */
  dt = (colorbar.w-1)/(NUMCOLORS+1);
  /* desenha a cor de fundo */
  cdForeground(getcolor(colorbar.bci));
  cdBox(1, (int)(2.0*dt/3.0), 1, (int)(2.0*(colorbar.h-1)/3.0));
  hollowbox(1, (int)(2.0*dt/3.0), 1, (int)(2.0*(colorbar.h-1)/3.0));
  /* desenha a cor de frente */
  cdForeground(getcolor(colorbar.fci));
  cdBox((int)(dt/3.0), (int)(dt)-1, (int)((colorbar.h-1)/3.0)+1, colorbar.h-1);
  hollowbox((int)(dt/3.0), (int)(dt)-1, (int)((colorbar.h-1)/3.0)+1, colorbar.h-1);
 
  /* restaura o canvas anteriormente ativo */
  cdActivate(colorbar.other_canvas);

  return IUP_DEFAULT;
}

/*-------------------------------------------------------------------------*/
/* Funcao associada ao botao do mouse.                                     */
/*-------------------------------------------------------------------------*/
static int fColorBarButtonCB(Ihandle *self, int b, int e, int x, int y, char *r)
{
  int c;
 
  ignore(self);
  ignore(r);

  /* salva o canvas ativo no momento */
  colorbar.other_canvas = cdActiveCanvas();

  /* ativa o canvas da colorbar */
  if (cdActivate(colorbar.bar_canvas) == CD_ERROR) {
    printf("Color Bar Error: Unable to activate canvas.");
    return 1;
  }

  /* converte para coordenadas do canvas */
  cdUpdateYAxis(&y);

  /* se o botao foi pressionado */
  if (e) {

    /* acha onde foi o click */
    c = findwhere(x, y, 1, 16);
    
    /* se o click foi dentro de alguma cor... */
    if (c != 0) {

      /* botao da esquerda eh mudancca de cor de foreground */
      if (b == IUP_BUTTON1) {

        /* se for double-click */
        if ((isdouble(r)) && (c == colorbar.fci) ) {

          /* mostra o dialogo */
          color_change_show(c);
        }

        /* muda a cor de frente corrente */
        else {

          /* largura de cada celula */
          double dt;
          unsigned char r, g, b;
  
          /* altera a variavel do usuario */
          *(colorbar.p_foreground) = getcolor(c);
       
          /* altera o indice da cor de frente corrente */
          colorbar.fci = c;

          cdDecodeColor(getcolor(colorbar.fci), &r, &g, &b);
          sprintf(ctgc.status_line, "cdForeground(cdEncodeColor(%d, %d, %d))", (int)r, (int)g, (int)b);
          set_status();

          /* altera o feedback no primeiro elemento da palheta */
          dt = (colorbar.w-1)/(NUMCOLORS+1);
          cdForeground(getcolor(colorbar.fci));
          cdBox((int)(dt/3.0), (int)(dt)-1, (int)((colorbar.h-1)/3.0)+1, colorbar.h-1);
          hollowbox((int)(dt/3.0), (int)(dt)-1, (int)((colorbar.h-1)/3.0)+1, colorbar.h-1);
        }

      }

      else if (b == IUP_BUTTON3) {
          
        /* largura de cada celula */
        double dt;
        unsigned char r, g, b;

        /* altera a variavel do usuario */
        *(colorbar.p_background) = getcolor(c);
      
        /* altera o indice da cor de frente corrente */
        colorbar.bci = c;
     
        cdDecodeColor(getcolor(colorbar.bci), &r, &g, &b);
        sprintf(ctgc.status_line, "cdBackground(cdEncodeColor(%d, %d, %d))", (int)r, (int)g, (int)b);
        set_status();

        /* altera o feedback no primeiro elemento da palheta */
        dt = (colorbar.w-1)/(NUMCOLORS+1);
        cdForeground(getcolor(colorbar.bci));
        cdBox(1, (int)(2.0*dt/3.0), 1, (int)(2.0*(colorbar.h-1)/3.0));
        hollowbox(1, (int)(2.0*dt/3.0), 1, (int)(2.0*(colorbar.h-1)/3.0));
        cdForeground(getcolor(colorbar.fci));
        cdBox((int)(dt/3.0), (int)(dt)-1, (int)((colorbar.h-1)/3.0)+1, colorbar.h-1);
        hollowbox((int)(dt/3.0), (int)(dt)-1, (int)((colorbar.h-1)/3.0)+1, colorbar.h-1);
      }

    }

  }

  /* restaura o canvas anteriormente ativo */
  cdActivate(colorbar.other_canvas);
  
  return IUP_DEFAULT;
}

/*-------------------------------------------------------------------------*/
/* Callback associado ao resize do canvas.                                 */
/*-------------------------------------------------------------------------*/
static int fColorBarResizeCB(Ihandle *self, int w, int h)
{
  ignore(self);

  /* atualiza as dimensoes do canvas */
  colorbar.w = w;
  colorbar.h = h;

  /* atualiza as fronteiras dos elementos da palheta */
  bounds(colorbar.w, NUMCOLORS+1, colorbar.bounds); 

  return IUP_DEFAULT;
}

/*-------------------------------------------------------------------------*/
/* Confirma a mudanca de cores.                                            */
/*-------------------------------------------------------------------------*/
static int color_change_ok(void)
{
  int r, g, b, a;
  long new_color;

  /* pega os novos valores */
  r = IupGetInt(color_change.red, IUP_VALUE);
  g = IupGetInt(color_change.green, IUP_VALUE);
  b = IupGetInt(color_change.blue, IUP_VALUE);
  a = IupGetInt(color_change.alpha, IUP_VALUE);

  /* se todos forem validos */
  if ((r<256)&&(g<256)&&(b<256)) {

    /* esconde a caixa de dialogo */
    IupHide(color_change.dialog);

    /* atualiza a cor no contexto */ 
    new_color = cdEncodeColor((unsigned char)r, (unsigned char) g, (unsigned char) b);
    new_color = cdEncodeAlpha(new_color, (unsigned char)a);
    changecolor(color_change.to_change, new_color);

    colorbar.fci = color_change.to_change;

    /* redesenha a colorbar */
    fColorBarRepaint();

    /* altera a variavel do usuario */
    *(colorbar.p_foreground) = new_color;

  }

  return IUP_DEFAULT;
}

/*-------------------------------------------------------------------------*/
/* Inicializa a ColorBar.                                                  */
/*-------------------------------------------------------------------------*/
void ColorBarClose(void)
{
  cdKillCanvas(colorbar.bar_canvas);
  IupDestroy(color_change.dialog);
}

int ColorBarInit(Ihandle *parent, Ihandle *canvas, long *foreground, long *background)
{
  char *bg_color;

  /* pega a cor de fundo do dialogo parente */
  bg_color = IupGetAttribute(parent, "BGCOLOR");
  if (bg_color == NULL) {
    printf("Color Bar Error: Unable to get bg_color.");
    return 0;
  }
  sscanf(bg_color, "%d %d %d", &colorbar.bgr, &colorbar.bgg, &colorbar.bgb);

  /* inicializa as cores de fundo e de frente defaults */
  colorbar.fci = 1;
  colorbar.bci = 4;

  /* pega o enderecco das variaveis do usuario */
  colorbar.p_foreground = foreground;
  colorbar.p_background = background;

  /* inicializa a palheta interna de cores */
  resetcolors();

  /* cria o canvas do CD */
  colorbar.bar_canvas = cdCreateCanvas(CD_IUP, canvas);
  if (colorbar.bar_canvas == NULL) {
    printf("Color Bar Error: Unable to create canvas.");
    return 0;
  }

  /* salva o canvas ativo no momento */
  colorbar.other_canvas = cdActiveCanvas();

  /* ativa o canvas da colorbar */
  if (cdActivate(colorbar.bar_canvas) == CD_ERROR) {
    printf("Color Bar Error: Unable to activate canvas.");
    return 0;
  }

  /* pega as dimensoes do canvas pela primeira vez */
  cdGetCanvasSize(&colorbar.w, &colorbar.h, NULL, NULL);

  /* restaura o canvas anteriormente ativo */
  if (colorbar.other_canvas != NULL) cdActivate(colorbar.other_canvas);

  /* cria o vetor com as fronteiras dos elementos da palheta */
  bounds(colorbar.w, NUMCOLORS+1, colorbar.bounds); 
  
  /* associa os callbacks */
  IupSetFunction("cmdColorBarButtonCB", (Icallback) fColorBarButtonCB);
  IupSetFunction("cmdColorBarRepaint", (Icallback) fColorBarRepaint);
  IupSetFunction("cmdColorBarResizeCB", (Icallback) fColorBarResizeCB);

  /* desenha a barra de cores pela primeira vez */
  fColorBarRepaint();

  /* inicializa o dialogo de troca de cores da palheta */
  color_change.dialog = IupDialog(
    IupVbox(
      IupHbox(
        IupLabel("R:"),
        color_change.red = IupText("cmdInteger"),
        IupLabel("G:"),
        color_change.green = IupText("cmdInteger"),
        IupLabel("B:"),
        color_change.blue = IupText("cmdInteger"),
        IupLabel("A:"),
        color_change.alpha = IupText("cmdInteger"),
        NULL
      ),
      IupHbox(
        IupFill(),
        color_change.bt_ok = IupButton("OK", "cmdColorChangeOK"),
        color_change.bt_cancel = IupButton("Cancel", "cmdColorChangeCancel"),
        IupFill(),
        NULL
      ),
      NULL
    )
  );

  /* atributos do dialogo */
  IupSetAttribute(color_change.dialog, IUP_TITLE, "Color Change:");
  IupSetAttribute(color_change.dialog, IUP_MARGIN, "5x5");
  IupSetAttribute(color_change.dialog, IUP_GAP, "5");
  IupSetAttribute(color_change.dialog, "MAXBOX", "NO");
  IupSetAttribute(color_change.dialog, "MINBOX", "NO");
  IupSetAttribute(color_change.dialog, "PARENTDIALOG", "dlgMain");

  /* atributos dos texts */
  IupSetFunction("cmdInteger", (Icallback) integer);
  IupSetAttribute(color_change.red, "NC", "3");
  IupSetAttribute(color_change.green, "NC", "3");
  IupSetAttribute(color_change.blue, "NC", "3");
  IupSetAttribute(color_change.alpha, "NC", "3");
  IupSetAttribute(color_change.red, "SIZE", "24");
  IupSetAttribute(color_change.green, "SIZE", "24");
  IupSetAttribute(color_change.blue, "SIZE", "24");
  IupSetAttribute(color_change.alpha, "SIZE", "24");
  
  /* atributos dos botoes */
  IupSetAttribute(color_change.bt_ok, IUP_SIZE, "30");
  IupSetAttribute(color_change.bt_cancel, IUP_SIZE, "30");
  IupSetFunction("cmdColorChangeCancel", (Icallback) color_change_cancel);
  IupSetFunction("cmdColorChangeOK", (Icallback) color_change_ok);

  return 1;
}        
