/* showimagemode.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2001-2025 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 "showimagemode.h"
#include "listermode.h"
#include "worker_locale.h"
#include "wconfig.h"
#include "wc_color.hh"
#include "worker.h"
#include "configheader.h"
#include "configparser.hh"
#include "fileentry.hh"
#include "nmspecialsourceext.hh"
#include "aguix/stringgadget.h"
#include "aguix/button.h"
#include "aguix/fieldlistview.h"
#include "aguix/choosebutton.h"
#include "aguix/awindow.h"
#include "datei.h"
#include "simplelist.hh"

const char *defaultviewers[] = { "xliwrapper_worker",
                                 "displaywrapper_worker" };

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

ShowImageMode::ShowImageMode(Lister *parent):ListerMode(parent)
{
  x=0;
  y=0;
  w=1;
  h=1;
  imgwin=NULL;
  lastactivefe=NULL;
  show_program = dupstring( defaultviewers[0] );
  blanked=false;
}

ShowImageMode::~ShowImageMode()
{
  clearLastActiveFE();
  _freesafe(show_program);
}

void ShowImageMode::messageHandler(AGMessage *msg)
{
  int tx,ty,tw,th;
  bool ma=false;
  switch(msg->type) {
    case AG_SIZECHANGED:
      parentlister->getGeometry(&tx,&ty,&tw,&th);
      reconf(tx,ty,tw,th);
      break;
  case AG_EXPOSE:
      if ( imgwin ) {
          if ( msg->expose.window == imgwin->getWindow() ) {
#ifdef HAVE_IMLIB2
              if ( m_use_internal && m_buffer ) {
                  imlib_context_set_image(m_buffer);
                  imlib_render_image_on_drawable( 0, 0 );
              }
#endif
          }
      }
      break;
  }
  if(ma==true) parentlister->makeActive();
}

void ShowImageMode::on()
{
  parentlister->getGeometry(&x,&y,&w,&h);

  if ( w < 10 ) w = 10;
  if ( h < 10 ) h = 10;
  imgwin = new AWindow( aguix, x, y, w, h, "" );
  parentawindow->add( imgwin );
  imgwin->create();
  imgwin->show();
  
  parentlister->setActiveMode(this);
  setName();
}

/* this is optional code to free X resources created by
 * xli. It's kind of a hack, but should be safe since
 * pretty much no-one else set the necessary property
 * in our own window */
#define FREE_XLI_RESOURCE

#ifdef FREE_XLI_RESOURCE

#define RETAIN_PROP_NAME "_XSETROOT_ID"

// that's a ugly hack from xli to free its pixmap resource
static void free_previous_xli_resource( Display *dsp, Window win )
{
    Pixmap *pm;
    Atom actual_type;
    int format;
    unsigned long nitems;
    unsigned long bytes_after;

    Atom atom = XInternAtom( dsp, RETAIN_PROP_NAME, 0 );

    if ( XGetWindowProperty( dsp, win, atom, 0, 1, 1,
                             AnyPropertyType,
                             &actual_type,
                             &format,
                             &nitems,
                             &bytes_after,
                             (unsigned char**)&pm) == Success ) {

        if ( actual_type == XA_PIXMAP &&
             format == 32 &&
             nitems == 1 &&
             bytes_after == 0 &&
             pm != NULL ) {

            XKillClient( dsp, (XID)*pm );
            XFree( pm );
        }
    }
}
#endif

void ShowImageMode::off()
{
  parentlister->setActiveMode(NULL);
  parentlister->setName("");
#ifdef FREE_XLI_RESOURCE
  free_previous_xli_resource( aguix->getDisplay(), imgwin->getWindow() );
#endif
  delete imgwin;
  imgwin=NULL;
  clearLastActiveFE();

  destroy_internal_viewer();
}

void ShowImageMode::reconf(int tx,int ty,int tw,int th)
{
  if((tw!=w)||(th!=h)||(tx!=x)||(ty!=y)) {
    w=tw;
    h=th;
    if ( w < 10 ) w = 10;
    if ( h < 10 ) h = 10;
    x=tx;
    y=ty;
    imgwin->resize(w,h);
    imgwin->move(x,y);
    update(true);
  }
}

void ShowImageMode::activate()
{
}

void ShowImageMode::deactivate()
{
}

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

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

const char *ShowImageMode::getStaticType()
{
  return type;
}

int ShowImageMode::configure()
{
  Button *db;
  AWindow *win;
  StringGadget *sg;
  AGMessage *msg;
  int endmode=-1;
  char *tstr;
  Requester *req;
  
  tstr = (char*)_allocsafe( strlen( catalog.getLocale( 293 ) ) + strlen( getLocaleName() ) + 1 );
  sprintf( tstr, catalog.getLocale( 293 ), getLocaleName() );
  win = new AWindow( aguix, 10, 10, 10, 10, tstr, AWindow::AWINDOW_DIALOG );
  win->create();
  _freesafe(tstr);

  AContainer *ac1 = win->setContainer( new AContainer( win, 1, 5 ), true );
  ac1->setMinSpace( 5 );
  ac1->setMaxSpace( 5 );

  ChooseButton *internal_cb = nullptr;
#ifdef HAVE_IMLIB2
  internal_cb = ac1->addWidget( new ChooseButton( aguix, 0, 0,
                                                  m_use_internal,
                                                  catalog.getLocale( 1608 ),
                                                  LABEL_RIGHT, 0 ), 0, 0, AContainer::CO_INCWNR );
#else
  ac1->setMaxHeight( 0, 0, 0 );
  m_use_internal = false;
#endif

  AContainer *info_text_ac = nullptr;
  win->addMultiLineText( catalog.getLocale( 521 ), *ac1, 0, 1, &info_text_ac, NULL );

  sg = ac1->addWidget( new StringGadget( aguix, 0, 0, 100, show_program, 0 ), 0, 2, AContainer::CO_INCW );
  
  db = ac1->addWidget( new Button( aguix, 0, 0, 100, catalog.getLocale( 110 ), 0 ), 0, 3, AContainer::CO_INCW );

  AContainer *ac1_2 = ac1->add( new AContainer( win, 2, 1 ), 0, 4 );
  ac1_2->setMinSpace( 5 );
  ac1_2->setMaxSpace( -1 );
  ac1_2->setBorderWidth( 0 );
  Button *okb =(Button*)ac1_2->add( new Button( aguix,
                                                0,
                                                0,
                                                catalog.getLocale( 11 ),
                                                0 ), 0, 0, AContainer::CO_FIX );
  Button *cb = (Button*)ac1_2->add( new Button( aguix,
						0,
						0,
						catalog.getLocale( 8 ),
						0 ), 1, 0, AContainer::CO_FIX );

  win->setDoTabCycling( true );
  win->contMaximize( true );
  win->show();

  if ( m_use_internal ) {
      info_text_ac->hide();
      sg->hide();
      db->hide();
  }

  req=new Requester(aguix);
  for(;endmode==-1;) {
    msg=aguix->WaitMessage(win);
    if(msg!=NULL) {
      switch(msg->type) {
        case AG_CLOSEWINDOW:
          if(msg->closewindow.window==win->getWindow()) endmode=1;
          break;
        case AG_BUTTONCLICKED:
          if(msg->button.button==okb) {
            endmode = 0;
          } else if(msg->button.button==cb) endmode=1;
          else if(msg->button.button==db) {
            tstr = requestDefaultViewer();
            if ( tstr != NULL ) {
              sg->setText( tstr );
              _freesafe( tstr );
            }
          }
          break;
      case AG_CHOOSECLICKED:
          if ( msg->choose.button == internal_cb ) {
              if ( msg->choose.state ) {
                  info_text_ac->hide();
                  sg->hide();
                  db->hide();
              } else {
                  info_text_ac->show();
                  sg->show();
                  db->show();
              }
          }
      }
      aguix->ReplyMessage(msg);
    }
  }
  delete req;
  
  if(endmode==0) {
    // ok
    _freesafe(show_program);
    show_program=dupstring(sg->getText());
    if ( internal_cb ) {
        m_use_internal = internal_cb->getState();
    }
  }
  
  delete win;

  return endmode;
}

void ShowImageMode::cyclicfunc(cyclicfunc_mode_t mode)
{
  update(false);
}

const char* ShowImageMode::getLocaleName()
{
  return getStaticLocaleName();
}

const char* ShowImageMode::getStaticLocaleName()
{
  return catalog.getLocale(174);
}

int ShowImageMode::load()
{
    for (;;) {
        if ( worker_token == VIEWSTR_WCP ) {
            readtoken();

            if ( worker_token != '=' ) return 1;
            readtoken();

            if ( worker_token != STRING_WCP ) return 1;
            if ( show_program != NULL ) _freesafe( show_program );
            show_program = dupstring( yylval.strptr );
            readtoken();

            if ( worker_token != ';' ) return 1;
            readtoken();
        } else if ( worker_token == INTERNALVIEWER_WCP ) {
            readtoken();

            if ( worker_token != '=' ) return 1;
            readtoken();

            if ( worker_token == YES_WCP ) {
                m_use_internal = true;
            } else if ( worker_token == NO_WCP ) {
                m_use_internal = false;
            } else {
                return 1;
            }
            readtoken();

            if ( worker_token != ';' ) return 1;
            readtoken();
        } else {
            break;
        }
    }
    return 0;
}

bool ShowImageMode::save(Datei *fh)
{
  if ( fh == NULL ) return false;
  
  fh->configPutPairString( "viewstr", show_program );
#ifdef HAVE_IMLIB2
  if ( ! m_use_internal ) {
      fh->configPutPairBool( "internalviewer", m_use_internal );
  }
#endif

  return false;
}

void ShowImageMode::setName()
{
  parentlister->setName(catalog.getLocale(174));
}

void ShowImageMode::relayout()
{
}

void ShowImageMode::update(bool force)
{
    ListerMode *lm1=NULL;
    Lister *ol=NULL;
    List *colors;
    WC_Color *bgcol;
    const FileEntry *fe;
    char *buf,*colbuf, *buf2;
    std::string str1;

    ol=parentlister->getWorker()->getOtherLister(parentlister);
    if(ol!=NULL) {
        lm1=ol->getActiveMode();
    }
  
    if(force==true) clearLastActiveFE();

    if ( ! lm1 ) return;

    std::list< NM_specialsourceExt > files;
    
    lm1->getSelFiles( files, ListerMode::LM_GETFILES_ONLYACTIVE );
    if ( files.empty() ) {
        clearLastActiveFE();
        blank();
        return;
    }

    fe = files.begin()->entry();
    if ( fe->equals( lastactivefe ) ) {
        return;
    }

    clearLastActiveFE();

    if ( fe != NULL ) {
        lastactivefe = new FileEntry( *fe );
    }

    init_internal_viewer();

#ifdef HAVE_IMLIB2
    if ( ! m_use_internal ) {
#endif
        // first get the background color in the X format for xli
        colors=(List*)wconfig->getColors();
        bgcol=(WC_Color*)colors->getElementAt(0);
        colbuf = (char*)_allocsafe( strlen( "rgb://" ) + ( 3 * A_BYTESFORNUMBER( int ) ) + 1 );
        sprintf( colbuf, "rgb:%x/%x/%x", bgcol->getRed(), bgcol->getGreen(), bgcol->getBlue() );
        
        if ( show_program[0] != '/' ) {
            // no absolute path so check if we find
            // the file in the script directory
            str1 = Worker::getDataDir();
            str1 += "/scripts/";
            str1 += show_program;
            if ( Datei::fileExistsExt( str1.c_str() ) != Datei::D_FE_FILE ) {
                // not found so let the shell find it
                str1 = show_program;
            }
        } else {
            str1 = show_program;
        }

        buf = (char*)_allocsafe( strlen( "%s %d %d %d %d %x " ) +
                                 strlen( str1.c_str() ) +
                                 4 * A_BYTESFORNUMBER( int ) + 
                                 A_BYTESFORNUMBER( Window ) + 1 );
        sprintf( buf, "%s %d %d %d %d %x ", str1.c_str(), 0, 0, w, h, (unsigned int)imgwin->getWindow() );
        buf2 = AGUIX_catTrustedAndUnTrusted( buf, lastactivefe->fullname );
        _freesafe( buf );
        buf = catstring( buf2, " " );
        _freesafe( buf2 );
        buf2 = catstring( buf, colbuf );
        _freesafe( buf );
        buf = buf2;
        blank();
        int tres __attribute__((unused)) = system( buf );
        blanked=false;
          
        _freesafe(buf);
        _freesafe(colbuf);
#ifdef HAVE_IMLIB2
    } else {
        Imlib_Image image;

        blank();

        imlib_context_set_blend(0);
        imlib_context_set_color_modifier(NULL);
        imlib_context_set_drawable( imgwin->getWindow() );
        image = imlib_load_image( lastactivefe->fullname );
        if ( image ) {
            imlib_context_set_image(image);
            imlib_image_set_changes_on_disk();
            int iw = imlib_image_get_width();
            int ih = imlib_image_get_height();

            if ( iw > 0 && ih > 0 ) {
                double scale_w = (double)w / (double)iw;
                double scale_h = (double)h / (double)ih;
                int target_w, target_h;

                if ( scale_w < scale_h ) {
                    target_w = w;
                    target_h = floor( ih * scale_w );
                } else {
                    target_h = h;
                    target_w = floor( iw * scale_h );
                }

                if ( m_buffer ) {
                    imlib_context_set_image(m_buffer);
                    imlib_free_image();
                    m_buffer = nullptr;
                }

                m_buffer = imlib_create_image( target_w, target_h );
                imlib_context_set_blend(0);

                imlib_context_set_image(m_buffer);
                imlib_blend_image_onto_image(image, 0,
                                             0, 0, iw, ih,
                                             0, 0, target_w, target_h);
                imlib_context_set_image(image);
                imlib_free_image();
                imlib_context_set_blend(0);
                imlib_context_set_image(m_buffer);
                imlib_render_image_on_drawable(0,0);
                blanked = false;
            } else {
                imlib_free_image();
            }
        }
    }
#endif
}

void ShowImageMode::blank()
{
  if(blanked==false) {
    XSetWindowAttributes attr;
    attr.background_pixel=aguix->getPixel(0);
    attr.background_pixmap=None;
    XChangeWindowAttributes(aguix->getDisplay(),imgwin->getWindow(),CWBackPixel|CWBackPixmap,&attr);
    XClearWindow(aguix->getDisplay(),imgwin->getWindow());
    aguix->Flush();
  }
  blanked=true;
}

char *ShowImageMode::requestDefaultViewer()
{
  Button *okb,*cb;
  AWindow *win;
  int tw,ttw,tth,ttx,tty;
  AGMessage *msg;
  int endmode=-1;
  GUIElement *bs[2];
  FieldListView *lv;
  unsigned int i;
  int row;
  char *result;
  std::string str1;
  
  ttw = tth = 10;
  ttx = tty = 5;

  win = new AWindow( aguix, 10, 10, ttw, tth, catalog.getLocale( 534 ), AWindow::AWINDOW_DIALOG );
  win->create();

  win->addTextFromString( catalog.getLocale( 535 ),
                          ttx, tty, 5,
                          NULL, NULL, &tty );
  tty += 5;

  lv = (FieldListView*)win->add( new FieldListView( aguix,
                                                   ttx, tty,
                                                   10, 6 * aguix->getCharHeight(),
                                                   0 ) );
  for ( i = 0; i < ( sizeof( defaultviewers ) / sizeof( defaultviewers[0] ) ); i++ ) {
    row = lv->addRow();
    lv->setText( row, 0, defaultviewers[i] );
    lv->setPreColors( row, FieldListView::PRECOLOR_ONLYACTIVE );
    if ( i == 0 ) lv->setActiveRow( row );
  }
  tty += lv->getHeight() + 5;

  lv->setVBarState( 2 );
  lv->setHBarState( 0 );
  lv->setDisplayFocus( true );
  lv->setAcceptFocus( true );
  lv->maximizeX();

  okb = (Button*)win->add( new Button( aguix,
                                       5,
                                       tty,
                                       catalog.getLocale( 11 ),
                                       0 ) );
  cb = (Button*)win->add( new Button( aguix,
                                      5,
                                      tty,
                                      catalog.getLocale( 8 ),
                                      0 ) );
  tty += okb->getHeight() + 5;

  win->maximizeX();
  
  ttw = win->getWidth();
  
  bs[0] = okb;
  bs[1] = cb;
  tw = AGUIX::scaleElementsW( ttw, 5, 5, -1, false, false, bs, NULL, 2 );

  win->resize( ( tw > ttw ) ? tw : ttw, tty );
  lv->resize( win->getWidth() - 2 * win->getBorderWidth(), lv->getHeight() );

  win->setDoTabCycling( true );
  win->setMaxSize( win->getWidth(), win->getHeight() );
  win->setMinSize( win->getWidth(), win->getHeight() );
  win->show();
  
  result = NULL;
  
  for(;endmode == -1;) {
    msg = aguix->WaitMessage( win );
    if ( msg != NULL ) {
      switch ( msg->type ) {
        case AG_CLOSEWINDOW:
          if ( msg->closewindow.window == win->getWindow() ) endmode = 1;
          break;
        case AG_BUTTONCLICKED:
          if ( msg->button.button == okb ) endmode = 0;
          else if ( msg->button.button == cb ) endmode = 1;
          break;
      }
      aguix->ReplyMessage( msg );
    }
  }
  
  if ( endmode == 0 ) {
    // ok
    row = lv->getActiveRow();
    if ( lv->isValidRow( row ) == true ) {
      str1 = lv->getText( row, 0 );
      if ( str1.length() > 0 ) {
        result = dupstring( str1.c_str() );
      }
    }
  }
  
  delete win;

  return result;
}

void ShowImageMode::clearLastActiveFE() {
  if ( lastactivefe != NULL ) {
    delete lastactivefe;
    lastactivefe = NULL;
  }
}

static int worker_imlib_progress( Imlib_Image im,
                                  char percent,
                                  int update_x, int update_y,
                                  int update_w, int update_h)
{
    Worker::getAGUIX()->doXMsgs( NULL, true );

    if ( Worker::getAGUIX()->getLastKeyRelease() == XK_Escape ) {
        return 0;
    }

    return 1;
}

void ShowImageMode::init_internal_viewer()
{
#ifdef HAVE_IMLIB2
  if ( m_use_internal && ! m_imlib_initialized ) {
      imlib_context_set_display( aguix->getDisplay());
      imlib_context_set_visual( DefaultVisual( aguix->getDisplay(), aguix->getScreen() ) );
      imlib_context_set_colormap( aguix->getColormap() );
      imlib_set_cache_size( 2048 * 1024 );
      imlib_set_color_usage( 128 );
      imlib_context_set_dither( 1 );
      imlib_context_set_progress_function( worker_imlib_progress );

      m_imlib_initialized = true;
  }
#endif
}

void ShowImageMode::destroy_internal_viewer()
{
#ifdef HAVE_IMLIB2
    if ( m_imlib_initialized ) {
        imlib_set_cache_size(0);
        if ( m_buffer ) {
            imlib_context_set_image( m_buffer );
            imlib_free_image();
            m_buffer = nullptr;
        }
        m_imlib_initialized = false;
    }
#endif
}
