/* button.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2001-2022 Ralf Hoffmann.
 * You can contact me at: ralf@boomerangsworld.de
 *   or http://www.boomerangsworld.de/worker
 *
 * 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 of the License, 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 St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "button.h"
#include "awindow.h"
#include "guielement.h"
#include "drawablecont.hh"
#include "aicon.hh"

const char *Button::type="Button";

Button::~Button()
{
  if(text[0]!=NULL) _freesafe(text[0]);
  if(text[1]!=NULL) _freesafe(text[1]);
}

Button::Button( AGUIX *taguix, int tx, int ty, int width,
                const char *ttext, int tdata ) : GUIElement( taguix )
{
    int bw;

    init_class();
  
    bw = 2;

    _x = tx;
    _y = ty;
    if ( width > 0 ) _w = width;
    _h = taguix->getCharHeight() + 2 * bw;

    applyFaces();

    data = tdata;
    _freesafe( text[0] );
    text[0] = dupstring( ttext );
}

Button::Button(AGUIX *taguix,int tx,int ty,
               const char *ttext,int tfg,int tbg,int tdata):GUIElement(taguix)
{
  int bw;

  init_class();
  
  bw = 2;

  _x = tx;
  _y = ty;
  _w = taguix->getTextWidth( ttext ) + taguix->getTextWidth( "  " );
  _h = taguix->getCharHeight() + 2 * bw;

  data = tdata;
  _freesafe( text[0] );
  text[0] = dupstring( ttext );
  fg[0] = tfg;
  fg[1] = 0;
  bg[0] = tbg;
  bg[1] = 0;

  applyFaces();
}

Button::Button( AGUIX *taguix, int tx, int ty,
                const char *ttext,
                const std::string &tfg_name,
                const std::string &tbg_name,
                int tdata ) : GUIElement( taguix )
{
    int bw;

    init_class();
  
    bw = 2;

    _x = tx;
    _y = ty;
    _w = taguix->getTextWidth( ttext ) + taguix->getTextWidth( "  " );
    _h = taguix->getCharHeight() + 2 * bw;

    data = tdata;
    _freesafe( text[0] );
    text[0] = dupstring( ttext );
    fg[0] = _aguix->getFaces().getColor( tfg_name );
    fg[1] = 0;
    bg[0] = _aguix->getFaces().getColor( tbg_name );
    bg[1] = 0;

    applyFaces();
}


Button::Button(AGUIX *taguix,int tx,int ty,
               const char *ttext, int tdata ) : GUIElement( taguix )
{
    int bw;

    init_class();
  
    bw = 2;

    _x = tx;
    _y = ty;
    _w = taguix->getTextWidth( ttext ) + taguix->getTextWidth( "  " );
    _h = taguix->getCharHeight() + 2 * bw;

    data = tdata;
    _freesafe( text[0] );
    text[0] = dupstring( ttext );

    applyFaces();
}

Button::Button(AGUIX *taguix,int tx,int ty,int width,int height,
               const char *ttext,int tfg,int tbg,int tdata):GUIElement(taguix)
{
  init_class();
  
  _x = tx;
  _y = ty;
  if ( width > 0 ) _w = width;
  if ( height > 0 ) _h = height;

  data = tdata;
  _freesafe( text[0] );
  text[0] = dupstring( ttext );
  fg[0] = tfg;
  fg[1] = 0;
  bg[0] = tbg;
  bg[1] = 0;

  applyFaces();
}

Button::Button(AGUIX *taguix,int tx,int ty,int width,int height,
               const char *ttext, int tdata ) : GUIElement( taguix )
{
    init_class();
  
    _x = tx;
    _y = ty;
    if ( width > 0 ) _w = width;
    if ( height > 0 ) _h = height;

    data = tdata;
    _freesafe( text[0] );
    text[0] = dupstring( ttext );

    applyFaces();
}

Button::Button(AGUIX *taguix,int tx,int ty,int width,
               const char *text_normal,const char *text_high,
               int fg_normal,int fg_high,
               int bg_normal,int bg_high,int tdata):GUIElement(taguix)
{
  int bw;

  init_class();
  
  bw = 2;

  _x = tx;
  _y = ty;
  if ( width > 0 ) _w = width;
  _h = taguix->getCharHeight() + 2 * bw;

  dual = true;
  data = tdata;
  _freesafe( text[0] );
  _freesafe( text[1] );
  text[0] = dupstring( text_normal );
  text[1] = dupstring( text_high );
  fg[0] = fg_normal;
  fg[1] = fg_high;
  bg[0] = bg_normal;
  bg[1] = bg_high;

  applyFaces();
}

Button::Button(AGUIX *taguix,int tx,int ty,int width,
               const char *text_normal,const char *text_high,
               int tdata ) : GUIElement( taguix )
{
    int bw;

    init_class();
  
    bw = 2;

    _x = tx;
    _y = ty;
    if ( width > 0 ) _w = width;
    _h = taguix->getCharHeight() + 2 * bw;

    dual = true;
    data = tdata;
    _freesafe( text[0] );
    _freesafe( text[1] );
    text[0] = dupstring( text_normal );
    text[1] = dupstring( text_high );

    applyFaces();
}

Button::Button( AGUIX *taguix, int tx, int ty,
                const char *text_normal, const char *text_high,
                int fg_normal, int fg_high,
                int bg_normal, int bg_high, int tdata ) : GUIElement( taguix )
{
    int bw;
    
    init_class();
    
    bw = 2;
    
    _x = tx;
    _y = ty;

    _w = taguix->getTextWidth( text_normal ) + taguix->getTextWidth( "  " );
    int tw = taguix->getTextWidth( text_high ) + taguix->getTextWidth( "  " );
    if ( tw > _w ) _w = tw;

    _h = taguix->getCharHeight() + 2 * bw;
    
    dual = true;
    data = tdata;
    _freesafe( text[0] );
    _freesafe( text[1] );
    text[0] = dupstring( text_normal );
    text[1] = dupstring( text_high );
    fg[0] = fg_normal;
    fg[1] = fg_high;
    bg[0] = bg_normal;
    bg[1] = bg_high;

    applyFaces();
}

Button::Button( AGUIX *taguix, int tx, int ty,
                const char *text_normal, const char *text_high,
                int tdata ) : GUIElement( taguix )
{
    int bw;
    
    init_class();
    
    bw = 2;
    
    _x = tx;
    _y = ty;

    _w = taguix->getTextWidth( text_normal ) + taguix->getTextWidth( "  " );
    int tw = taguix->getTextWidth( text_high ) + taguix->getTextWidth( "  " );
    if ( tw > _w ) _w = tw;

    _h = taguix->getCharHeight() + 2 * bw;
    
    dual = true;
    data = tdata;
    _freesafe( text[0] );
    _freesafe( text[1] );
    text[0] = dupstring( text_normal );
    text[1] = dupstring( text_high );

    applyFaces();
}

const char *Button::getText(int index2) const
{
  if((index2<0)||(index2>1)) return NULL;
  return text[index2];
}

void Button::setText(int index2,const char *new_text)
{
  if((index2<0)||(index2>1)) return;
  if(text[index2]!=NULL) _freesafe(text[index2]);
  text[index2]=dupstring(new_text);
  redraw();
}

bool Button::getDualState() const
{
  return dual;
}

void Button::setDualState(bool new_dual)
{
  dual=new_dual;
  redraw();
}

void Button::setFG(int index2,int tfg)
{
    if ( index2 < 0 || index2 > 1 ) return;
    if ( tfg < _aguix->getNumberOfColorsForType( AGUIXColor::USER_COLOR ) && tfg >= 0 ) {
        this->fg[index2] = tfg;
    }
    redraw();
}

void Button::setFG( int index2, const std::string &fg_name)
{
    if ( index2 < 0 || index2 > 1 ) return;

    int tfg = _aguix->getFaces().getColor( fg_name );
    if ( tfg < _aguix->getNumberOfColorsForType( AGUIXColor::USER_COLOR ) && tfg >= 0 ) {
        this->fg[index2] = tfg;
    }
    redraw();
}

int Button::getFG(int index2) const
{
  if((index2<0)||(index2>1)) return 0;
  return fg[index2];
}

void Button::setBG(int index2,int tbg)
{
    if ( index2 < 0 || index2 > 1 ) return;
    if ( tbg < _aguix->getNumberOfColorsForType( AGUIXColor::USER_COLOR ) && tbg >= 0 ) {
        this->bg[index2] = tbg;
    }
    redraw();
}

void Button::setBG( int index2, const std::string &bg_name)
{
    if ( index2 < 0 || index2 > 1 ) return;

    int tbg = _aguix->getFaces().getColor( bg_name );
    if ( tbg < _aguix->getNumberOfColorsForType( AGUIXColor::USER_COLOR ) && tbg >= 0 ) {
        this->bg[index2] = tbg;
    }
    redraw();
}

int Button::getBG(int index2) const
{
  if((index2<0)||(index2>1)) return 0;
  return bg[index2];
}

int Button::getData() const
{
  return data;
}

void Button::setData(int tdata)
{
  this->data=tdata;
}

void Button::redraw()
{
  bool showe=false;
  int px1 = 0,
      py1 = 0,
      px2 = 0,
      py2 = 0,
      px3 = 0,
      py3 = 0;
  int dw,dw2;
  int dy;
  char *tstr;
  int ch;
  int len, strwidth;
  unsigned int ear_width = 0 ;
  int bw;
  int text_color;
  int background;

  if ( isCreated() == false ) return;
  if ( win == 0 ) return;

  // prepare window background depending on current state
  prepareBG();

  /* Clear the window */
  _aguix->ClearWin(win);
  
  /* calculate dimension of the turned-down corner */

  if ( ( dual == true ) && ( showdual == true ) && ( state != 2 ) ) {
    showe = true;

    dw=_w/4;
    dw2=(int)((double)(_h/3)*1.3);
    if( dw2 < dw ) dw = dw2;

    ear_width = dw;

    px1 = _w-dw;
    py1 = 0;
    px2 = _w-dw;
    py2 = dw-1;
    px3 = _w-1;
    py3 = dw-1;
  }

  /* draw the text */

  if ( ( dual == false ) || ( state != 2 ) ) {
    text_color = fg[0];
    background = bg[0];
  } else {
    text_color = fg[1];
    background = bg[1];
  }
  if(font==NULL) {
    ch=_aguix->getCharHeight();
  } else {
    ch=font->getCharHeight();
  }

  dy=_h/2-ch/2;

  if ( ( dual == false ) || ( state != 2 ) ) {
    tstr=dupstring(text[0]);
  } else {
    tstr=dupstring(text[1]);
  }

  bw = 2;

  int icon_width = 0;
  int icon_height = 0;
  int icon_space = 0;

  if ( m_icon ) {
      m_icon->update( win, text_color, background, false );

      icon_width = m_icon->w();
      icon_height = m_icon->h();
      icon_space = 5;
  }

  len = _aguix->getStrlen4Width( tstr, _w - 2 * bw - icon_width - icon_space, &strwidth, font );
  if ( len > 0 ) {
    if ( len >= (int)strlen( tstr ) ) len = (int)strlen( tstr );
    tstr[len] = '\0';

    int dx = _w / 2 - ( icon_width + icon_space + strwidth ) / 2;
    
    DrawableCont dc( _aguix, win );

    if ( m_icon_pos == ICON_RIGHT ) {
        _aguix->DrawText( dc, font, tstr, dx, dy, text_color );
    } else {
        _aguix->DrawText( dc, font, tstr, dx + icon_width + icon_space, dy, text_color );
    }

    if ( m_strike_out ) {
        _aguix->setFG( text_color );
        _aguix->DrawLine( win,
                          dx,
                          _h / 2,
                          dx + strwidth,
                          _h / 2);
    }

    if ( m_icon ) {
        if ( m_icon_pos == ICON_RIGHT ) {
            m_icon->draw( win,
                          dx + strwidth + icon_space,
                          _h / 2 - icon_height / 2 );
        } else {
            m_icon->draw( win,
                          dx, _h / 2 - icon_height / 2 );
        }
    }
  } else {
      if ( m_icon ) {
          int dx = _w / 2 - icon_width / 2;
    
          m_icon->draw( win, dx, _h / 2 - icon_height / 2 );
      }
  }
  _freesafe( tstr );

  // draw the focus
  if ( ( getHasFocus() == true ) && ( getAcceptFocus() == true ) ) {
    _aguix->setDottedFG( 1 );
    _aguix->DrawDottedRectangle( win, bw, bw, _w - 2 * bw, _h - 2 * bw );
  }

  /* now draw the button */

  if( showe == true ) {
    _aguix->setFG( _aguix->getFaceCol_3d_bright() );
    _aguix->DrawTriangleFilled( win, px1, py1, px2, py2, px3, py3 );
    _aguix->setFG( _aguix->getFaceCol_default_bg() );
    _aguix->DrawTriangleFilled( win, px1, py1, _w-1, 0, px3, py3 );
  }

  if ( ! m_disabled ) {
      _aguix->drawBorder( win, ( state == 0 ) ? false : true, 0, 0, _w, _h, ear_width - 1 );
  }

  if( showe == true ) {
    _aguix->setFG( _aguix->getFaceCol_3d_dark() );
    _aguix->DrawLine( win, px1, py1, px2, py2 );
    _aguix->DrawLine( win, px2, py2, px3, py3 );
    _aguix->DrawLine( win, px1, py1, px3, py3 );
  }

  _aguix->Flush();
}

void Button::flush()
{
}

void Button::setState(int tstate)
{
  if((dual==false)&&(tstate==2)) {
    this->state=1;
  } else {
    this->state=tstate;
  }
  redraw(); 
}

int Button::getState() const
{
  return state;
}

bool Button::isInside(int px,int py) const
{
  if((px>0)&&(px<=_w)) {
    if((py>0)&&(py<=_h)) return true;
  }
  return false;
}

bool Button::handleMessage(XEvent *E,Message *msg)
{
    bool returnvalue;

    if ( isCreated() == false ) return false;

    returnvalue = GUIElement::handleMessage( E, msg );

    returnvalue=false;
    if((msg->type==ButtonPress)||(msg->type==ButtonRelease)) {
        if ( msg->window == win ) {
            if ( ( msg->button == Button3 ) ||
                 ( msg->button == Button1 ) ||
                 ( ( msg->button == Button4 ) && ( allowWheel == true ) ) ||
                 ( ( msg->button == Button5 ) && ( allowWheel == true ) ) ) {
                if(msg->type==ButtonPress) {
                    takeFocus();
                    if(msg->window==win) {
                        if( ( ( ( msg->button == Button1 ) || ( msg->button == Button4 ) || ( msg->button == Button5 ) ) && ( active[0] == true ) ) ||
                            ( ( msg->button == Button3 ) && ( active[1] == true ) ) ) {
                            AGMessage *agmsg = AGUIX_allocAGMessage();
                            agmsg->type=AG_BUTTONPRESSED;
                            agmsg->button.button=this;
                            if((msg->button==Button3)&&(dual==true)) {
                                setState(2);
                                instate=2;
                                agmsg->button.state=2;
                            } else {
                                switch ( msg->button ) {
                                    case Button4:
                                        instate = 3;
                                        break;
                                    case Button5:
                                        instate = 4;
                                        break;
                                    default:
                                        instate=1;
                                        break;
                                }
                                setState( instate );
                                agmsg->button.state = instate;
                            }

                            agmsg->button.keystate = KEYSTATEMASK( msg->keystate );

                            if ( ! m_disabled ) {
                                msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
                            } else {
                                delete agmsg;
                            }
                            returnvalue=true;
                        }
                    }
                } else {
                    if ( state != 0 && instate != 0 && ! m_disabled ) {
                        AGMessage *agmsg = AGUIX_allocAGMessage();
                        agmsg->type=AG_BUTTONRELEASED;
                        agmsg->button.button=this;
                        agmsg->button.state=instate;
                        agmsg->button.keystate = KEYSTATEMASK( msg->keystate );
                        msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
                        agmsg = AGUIX_allocAGMessage();
                        agmsg->type=AG_BUTTONCLICKED;
                        agmsg->button.button=this;
                        agmsg->button.state=instate;
                        agmsg->button.keystate = KEYSTATEMASK( msg->keystate );
                        msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
                    }
                    if(instate!=0) {
                        setState(0);
                        instate=0;
                        returnvalue=true;
                    }
                }
            }
        }
    } else if(msg->type==EnterNotify) {
        // alles hier und alles mit instate wird benutzt, damit Button sich anpaßt, wenn
        // Mauszeiger im Button oder außerhalb des Buttons ist
        if ( msg->window == win ) {
            if(instate!=0) {
                if(state!=instate) {
                    setState(instate);
                }
            }
        }
    } else if(msg->type==LeaveNotify) {
        // alles hier und alles mit instate wird benutzt, damit Button sich anpaßt, wenn
        // Mauszeiger im Button oder außerhalb des Buttons ist
        if ( msg->window == win ) {
            if(instate!=0) {
                setState(0);
            }
        }
    } else if(msg->type==Expose) {
        if ( msg->window == win ) {
            redraw();
        }
    } else if ( msg->type == KeyPress ) {
        if ( ( getAcceptFocus() == true ) && ( getHasFocus() == true ) ) {
            if ( msg->key == XK_space ) {
                if ( isVisible() == true ) {
                    if ( _parent->isTopParent( msg->window ) == true ) {
                        AGMessage *agmsg;

                        agmsg = AGUIX_allocAGMessage();
                        agmsg->type=AG_BUTTONCLICKED;
                        agmsg->button.button=this;
                        agmsg->button.state=1;
                        agmsg->button.keystate = KEYSTATEMASK( msg->keystate );

                        if ( ! m_disabled ) {
                            msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
                        } else {
                            delete agmsg;
                        }
                        if(instate!=0) {
                            setState(0);
                            instate=0;
                            returnvalue=true;
                        }
                    }
                }
            }
        }
    }
    if(returnvalue==true) {
        // jetzt noch die Message mit den Werten füllen
        msg->gadget=this;
        msg->gadgettype=BUTTON_GADGET;
    }
    //  return returnvalue;
    return false; // see cyclebutton.cc
}

bool Button::getShowDual() const
{
  return showdual;
}

void Button::setShowDual(bool new_val)
{
  if(showdual!=new_val) {
    showdual=new_val;
    redraw();
  } else showdual=new_val;
}

int Button::setFont( const char *fontname )
{
  font=_aguix->getFont(fontname);
  if(font==NULL) return -1;
  return 0;
}

const char *Button::getType() const
{
  return type;
}

bool Button::isType(const char *qtype) const
{
  if(strcmp(type,qtype)==0) return true;
  return false;
}

void Button::deactivate()
{
  deactivate(0);
  deactivate(1);
}

void Button::activate()
{
  activate(0);
  activate(1);
}

void Button::deactivate(int mode)
{
  if( ( mode == 0 ) || ( mode == 1 ) ) {
    active[mode]=false;
    if ( mode == 0 ) setAcceptFocus( false );
  }
}

void Button::activate(int mode)
{
  if( ( mode == 0 ) || ( mode == 1 ) ) {
    active[mode]=true;
  }
}

bool Button::getAllowWheel() const
{
  return allowWheel;
}

void Button::setAllowWheel( bool nv )
{
  allowWheel = nv;
}

void Button::init_class()
{
  dual = false;
  data = 0;
  state = 0;
  instate = 0;

  showdual = true;
  font = NULL;
  active[0] = true;
  active[1] = true;
  fg[0] = -1;
  fg[1] = -1;
  bg[0] = -1;
  bg[1] = -1;
  text[0] = dupstring( "" );
  text[1] = dupstring( "" );
  allowWheel = false;
  
  setCanHandleFocus();
  setAcceptFocus( true );
}

void Button::prepareBG( bool force )
{
  int newbg;

  if ( isCreated() == false ) return;
  if ( win == 0 ) return;

  if ( ( dual == false ) || ( state != 2 ) ) {
    newbg = bg[0];
  } else {
    newbg = bg[1];
  }
  
  _aguix->SetWindowBG( win, newbg );
}

int Button::getMaximumWidth()
{
    int max_w = _aguix->getTextWidth( text[0], font ) + _aguix->getTextWidth( "  ", font );

    if ( dual ) {
        int t_max_w = _aguix->getTextWidth( text[1], font ) + _aguix->getTextWidth( "  ", font );
        if ( t_max_w > max_w ) max_w = t_max_w;
    }

    return max_w;
}

void Button::applyFaces()
{
    if ( fg[0] < 0 ) {
        fg[0] = _aguix->getFaces().getColor( "button-fg" );
    }

    if ( bg[0] < 0 ) {
        bg[0] = _aguix->getFaces().getColor( "button-bg" );
    }

    if ( fg[1] < 0 ) {
        fg[1] = _aguix->getFaces().getColor( "button-alt-fg" );
    }

    if ( bg[1] < 0 ) {
        bg[1] = _aguix->getFaces().getColor( "button-alt-bg" );
    }
}

void Button::setIcon( const int *pixels,
                      size_t pixels_length,
                      icon_position pos )
{
    m_icon = std::make_shared< AIcon >( _aguix,
                                        win,
                                        pixels,
                                        pixels_length );
    m_icon_pos = pos;
}

int Button::getPreferredHeight()
{
    int bw = 2;

    if ( font == NULL ) {
        return _aguix->getCharHeight() + 2 * bw;
    } else {
        return font->getCharHeight() + 2 * bw;
    }
}

void Button::setDisabled( bool nv )
{
    m_disabled = nv;
}

bool Button::getDisabled() const
{
    return m_disabled;
}

void Button::setStrikeOut( bool nv )
{
    m_strike_out = nv;
}

bool Button::getStrikeOut() const
{
    return m_strike_out;
}

void Button::maximize( bool grow_only )
{
    int mw = getMaximumWidth();

    if ( ! grow_only || mw > getWidth() ) {
        resize( mw,
                getHeight() );
    }
}
