#include <iomanip>

// For ACLiC
#include <iostream>
#include <TSystem.h>
#include <TRegexp.h>
#include <TList.h>
#include <TCanvas.h>
#include <TButton.h>
#include <TText.h>
#include <TObjString.h>
#ifndef __CINT__
#include <TASImage.h>
#endif

void ProcessFile(const TString &n, const char *option, const char *text)
{
    Long_t size0, size1;
    Long_t id, flags, modtime;

    cout << text << " " << n.Data() << "... " << flush;
    gSystem->Exec(Form("cp -f %s %s~", n.Data(), n.Data()));
    gSystem->Exec(Form("jpegtran -optimize -copy all %s -outfile %s %s~", option, n.Data(), n.Data()));
    gSystem->GetPathInfo(n.Data(), &id, &size0, &flags, &modtime);
    gSystem->GetPathInfo(Form("%s~", n.Data()), &id, &size1, &flags, &modtime);
    cout << "Done. [" << Form("%.1f", size1/1024.) << "kB --> " << Form("%.1f", size0/1024.) << "kB] " << Form("%.1f%%", 100.*size0/size1) << endl;
}

void Process(TString &t, TString &n, const char *option, const char *text)
{
    TString thumb = Form("thumb_%s", t.Data());

    ProcessFile(thumb, option, text);
    ProcessFile(n,     option, text);
}

void ProcessImg(const char *name, const char *option, const char *text)
{
    TRegexp reg("IMG_[0-9]+");
    TString n     = name;
    TString core  = n(reg);
    TString dir;
    if (n.Last('/')>0)
        dir = n(0, n.Last('/')+1);

    TString thumb = Form("%sthumb_%s.jpg", dir.Data(), core.Data());

    ProcessFile(thumb, option, text);
    ProcessFile(name,  option, text);
}

TCanvas *GetDisplay()
{
    return (TCanvas*)gROOT->GetListOfCanvases()->FindObject("Display");
}

TList *GetList()
{
    return (TList*)GetDisplay()->FindObject("FileList");
}

void SetText(const char *txt)
{
    TCanvas *c = GetDisplay();

    c->cd(2);

    TText *n = (TText*)gPad->FindObject("Number");
    n->SetText(0.01, 0.01, txt);
    gPad->Update();
}

void SetupButtons(void *img, Int_t nimg, Bool_t on)
{
    TCanvas *c = GetDisplay();

    TString s[9];
    s[0] = Form("Action(0, (void*)%d, %d)", img, nimg+1);
    s[1] = Form("Action(0, (void*)%d, %d)", img, nimg-1);
    s[2] = Form("Action(0, (void*)%d, %d)", img, nimg+5);
    s[3] = Form("Action(0, (void*)%d, %d)", img, nimg-5);
    s[4] = Form("Action(1, (void*)%d, %d)", img, nimg);
    s[5] = Form("Action(2, (void*)%d, %d)", img, nimg);
    s[6] = Form("Action(3, (void*)%d, %d)", img, nimg);
    s[7] = Form("Action(4, (void*)%d, %d)", img, nimg);
    s[8] = Form("Action(5, (void*)%d, %d)", img, nimg);

    c->cd(2);

    TButton *next  = (TButton*)gPad->FindObject("Next");
    TButton *prev  = (TButton*)gPad->FindObject("Prev");
    TButton *next5 = (TButton*)gPad->FindObject("Next (+5)");
    TButton *prev5 = (TButton*)gPad->FindObject("Prev (-5)");
    TButton *rotr  = (TButton*)gPad->FindObject("Rotate ->");
    TButton *rotl  = (TButton*)gPad->FindObject("Rotate <-");
    TButton *fliph = (TButton*)gPad->FindObject("Flip Horz");
    TButton *flipv = (TButton*)gPad->FindObject("Flip Vert");
    TButton *optim = (TButton*)gPad->FindObject("Optimize");

    next ->SetMethod(on ? s[0].Data() : "Void()");
    prev ->SetMethod(on ? s[1].Data() : "Void()");
    next5->SetMethod(on ? s[2].Data() : "Void()");
    prev5->SetMethod(on ? s[3].Data() : "Void()");
    rotr ->SetMethod(on ? s[4].Data() : "Void()");
    rotl ->SetMethod(on ? s[5].Data() : "Void()");
    fliph->SetMethod(on ? s[6].Data() : "Void()");
    flipv->SetMethod(on ? s[7].Data() : "Void()");
    optim->SetMethod(on ? s[8].Data() : "Void()");
}

void Void() { }
void LoadImage(void *image, Int_t i)
{
    TCanvas  *c = GetDisplay();
    TList *list = GetList();

    SetupButtons(image, i, kFALSE);

    if (i>=list->GetSize())
        i=list->GetSize()-1;
    if (i<0)
        i=0;

    SetText("Loading...");

    TObject *obj = list->At(i);

    c->cd(1);

    TASImage &img = *(TASImage*)image;
    img.ReadImage(obj->GetName());
    img.Draw();

    TString desc = Form("»%s« (%dx%d)", obj->GetName(), img.GetWidth(), img.GetHeight());

    cout << "Image " << desc << endl;

    c->SetTitle(desc);
    c->Update();

    SetText(Form("Img: %d/%d", i+1, list->GetSize()));

    SetupButtons(image, i, kTRUE);
}

void Action(Int_t action, void *image, Int_t i)
{
    TString  name = GetList()->At(i)->GetName();
    switch (action)
    {
    case 1:
        SetText("Rotating...");
        ProcessImg(name, "-rotate 90", "Rotating");
        break;
    case 2:
        SetText("Rotating...");
        ProcessImg(name, "-rotate 270", "Rotating");
        break;
    case 3:
        SetText("Flipping...");
        ProcessImg(name, "-flip horizontal", "Flipping horizontal");
        break;
    case 4:
        SetText("Flipping...");
        ProcessImg(name, "-flip vertical", "Flipping vertical");
        break;
    case 5:
        SetText("Optimizing...");
        ProcessImg(name, "", "Optimizing");
        break;
    }
    LoadImage(image, i);
}

void SetupButton(TButton &b)
{
    b.SetName(b.GetTitle());
    b.SetBit(kCanDelete);
    b.Draw();
}

void CreateButtons()
{
    TButton *next =new TButton("Next",      "Void()",  0.01, 0.96, 0.99, 0.99);
    TButton *prev =new TButton("Prev",      "Void()",  0.01, 0.92, 0.99, 0.95);
    TButton *next5=new TButton("Next (+5)", "Void()",  0.01, 0.86, 0.99, 0.89);
    TButton *prev5=new TButton("Prev (-5)", "Void()",  0.01, 0.82, 0.99, 0.85);
    TButton *rotr =new TButton("Rotate ->", "Void()",  0.01, 0.76, 0.99, 0.79);
    TButton *rotl =new TButton("Rotate <-", "Void()",  0.01, 0.72, 0.99, 0.75);
    TButton *fliph=new TButton("Flip Horz", "Void()",  0.01, 0.66, 0.99, 0.69);
    TButton *flipv=new TButton("Flip Vert", "Void()",  0.01, 0.62, 0.99, 0.65);
    TButton *optim=new TButton("Optimize",  "Void()",  0.01, 0.56, 0.99, 0.59);
    //    TButton *quit=new TButton("Quit",       "Void()", 0.01, 0.48, 0.99, 0.51);

    SetupButton(*next);
    SetupButton(*prev);
    SetupButton(*next5);
    SetupButton(*prev5);
    SetupButton(*rotr);
    SetupButton(*rotl);
    SetupButton(*rotl);
    SetupButton(*fliph);
    SetupButton(*flipv);
    SetupButton(*optim);
}

void GetFiles(TString &dir, TList &list)
{
    TRegexp expr("^IMG_*.JPG$", kTRUE);

    if (!dir.EndsWith("/"))
        dir += "/";

    void *ptr = gSystem->OpenDirectory(dir);
    const char *name = 0;
    while ((name=gSystem->GetDirEntry(ptr)))
    {
        TString str(name);
        str = str(expr);

        if (!str.IsNull())
            list.Add(new TObjString(Form("%s%s", dir.Data(), name)));
    }
    gSystem->FreeDirectory(ptr);
}

TCanvas *CreateCanvas()
{
    TCanvas *c=new TCanvas("Display", "Image Display");
    c->SetBorderMode(0);
    c->SetWindowSize(889+4, 628);
    c->Divide(2, 1);

    c->cd(1);
    gPad->SetPad("", "", 0, 0, 0.9, 1, 1, 0, 0);

    c->cd(2);
    gPad->SetPad("", "", 0.91, 0, 1, 1, 0, 0, 0);

    TText *num=new TText;
    num->SetName("Number");
    num->SetBit(kCanDelete);
    num->SetTextSize(0.2);
    num->Draw();

    CreateButtons();

    return c;
}

void slideshow(TString dir=".", Bool_t optimizeall=kFALSE)
{
    gSystem->Load("libASImage.so");

    TList *list=new TList;
    list->SetName("FileList");
    list->SetBit(kCanDelete);
    GetFiles(dir, *list);

    if (optimizeall)
    {
        TIter Next(list);
        TObject *o=0;
        while ((o=Next()))
            ProcessImg(o->GetName(), "", "Optimizing");
        return;
    }

    TCanvas *c=CreateCanvas();

    c->cd();
    list->AppendPad();

    TASImage *img=new TASImage;
    img->SetBit(kCanDelete);

    LoadImage(img, 0);
}

