/*
 * Copyright (C) 2006,2007,2008,2009 Ronald Lamprecht
 *
 * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
 
#include "gui/LevelPackConfig.hh"

#include "errors.hh"
#include "gui/LevelPackComposer.hh"
#include "lev/Index.hh"
#include "main.hh"
#include "nls.hh"
#include "resource_cache.hh"
#include "video.hh"

using namespace ecl;
using namespace std;

namespace enigma { namespace gui {
    GroupButton::GroupButton(std::vector<std::string> groups, int pos) : 
            ValueButton(0, groups.size() - 1), 
            groupNames (groups), position (pos) {
        init();
    }
    
    int GroupButton::get_value() const  {
        return position; 
    }
    
    void GroupButton::set_value(int value) {
        position = value;
    }
    
    std::string GroupButton::get_text(int value) const {
        return groupNames[value];
    }
    
    /* ------------------- LevelmodeButton -------------------- */
    
    LevelmodeButton::LevelmodeButton(bool initialMode) : 
            ImageButton("ic-link_copy","ic-link_copy",this), mode (initialMode) {
        update();
    }
    
    bool LevelmodeButton::isLinkOnly() {
        return mode;
    }
    
    void LevelmodeButton::update() {
        if (mode)
            ImageButton::set_images("ic-link","ic-link_copy");
        else
            ImageButton::set_images("ic-link_copy","ic-link");
    }
    
    void LevelmodeButton::on_action(Widget *) 
    {
        mode = !mode;
        update();
        invalidate();
    }

    void LevelmodeButton::draw(ecl::GC &gc, const ecl::Rect &r) {
        update();
        ImageButton::draw(gc, r);
    }
    
    /* -------------------- Sokoball Button -------------------- */

    SokoballButton::SokoballButton(bool initialMode) : TextButton(this) {
        mode = initialMode;
    }

    void SokoballButton::on_action(Widget *) {
        mode = !mode;
        invalidate();
    }
    
    std::string SokoballButton::get_text() const {
        return mode ? N_("Yes") : N_("No");
    }
    
    bool SokoballButton::isSokoball() {
        return mode;
    }
    
    /* -------------------- LevelPackConfig -------------------- */
    
    LevelPackConfig::LevelPackConfig(std::string indexName, std::string groupName,
            bool forceGroupReasign) : titleTF (NULL), isReasignOnly (forceGroupReasign), 
            didEditMetaData (false), undo_quit (false) {
        const VMInfo &vminfo = *video_engine->GetInfo();
        const int vshrink = vminfo.width < 640 ? 1 : 0;
        
        if (indexName.empty())
            // new levelpack
            packIndex = new lev::PersistentIndex(" ", false, true, false,
                    INDEX_DEFAULT_PACK_LOCATION,  "", 
                    INDEX_STD_FILENAME, lev::Index::getCurrentGroup()); // mark as incomplete
        else
            packIndex = lev::Index::findIndex(indexName);
        ASSERT (packIndex != NULL, XFrontend, "not existing index Name");
        persIndex = dynamic_cast<lev::PersistentIndex *>(packIndex);
        isPersistent = (persIndex != NULL);
        isEditable = isPersistent ? persIndex->isUserEditable() : false;
        
        // build a list of allowed group
        std::vector<std::string> groups = lev::Index::getGroupNames();
        // eliminate pseudo group "All Packs"
        std::vector<std::string>::iterator itg = groups.begin();
        while (itg != groups.end()) {
            if (*itg == INDEX_ALL_PACKS) {
                itg = groups.erase(itg);
                break;
            }
            if (itg != groups.end())
                itg++;
        }
        // add pseudo group "[Every Group]"
        groups.push_back(std::string("[") + INDEX_EVERY_GROUP +"]");
        intialGroupPosition = groups.size() - 1;   // INDEX_EVERY_GROUP as default
        // mark index's default group with square brackets and find current group
        bool defaultGroupFound = false;
        for (unsigned i = 0; i < groups.size(); i++) {
            if (groups[i] == packIndex->getGroupName()) {
                intialGroupPosition = i;
            }
            if (groups[i] == packIndex->getDefaultGroupName()) {
                groups[i] = std::string("[") + groups[i] +"]";
                defaultGroupFound = true;
            }
        }
        if (!defaultGroupFound)  {
            groups.push_back(std::string("[") + packIndex->getDefaultGroupName() +"]");
        }
        groupButton = new GroupButton(groups, intialGroupPosition);

        // index location list setup
        std::vector<lev::Index *> * allIndices = lev::Index::getGroup(INDEX_ALL_PACKS);
        for (unsigned i = 0; i < allIndices->size(); i++)
            locationList.push_back((*allIndices)[i]->getName());
        position = -1;
        for (unsigned i = 0; i < locationList.size(); i++) {
            if (locationList[i] == indexName) {
                position = i;
                break;
            } 
        }
        oldPosition = position;
        if (position < 0) { 
            // append new levelpack as last
            locationList.push_back(indexName);
            position = locationList.size() - 1;
        }

        VList * titleLeftVList = new VList;
        titleLeftVList->set_spacing(vshrink?5:11);
        titleLeftVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
        titleLeftVList->set_default_size(vshrink?70:140, vshrink?17:35);
        Label * titleLabel = new Label(N_("Levelpack:"), HALIGN_RIGHT);
        Label * ownerLabel = new Label(N_("Owner:"), HALIGN_RIGHT);
        Label * groupLabel = new Label(N_("Group:"), HALIGN_RIGHT);
        Label * loactionLabel1 = new Label(N_("Location"), HALIGN_LEFT);
        Label * loactionLabel2 = new Label(N_("in [All Packs]:"), HALIGN_RIGHT);
        titleLeftVList->add_back(titleLabel);
        if (!isReasignOnly) {
           titleLeftVList->add_back(ownerLabel);
        }
        titleLeftVList->add_back(groupLabel);
        if (!isReasignOnly) {        
            titleLeftVList->add_back(new Label());
            titleLeftVList->add_back(loactionLabel1);
            titleLeftVList->add_back(loactionLabel2);
            titleLeftVList->add_back(new Label());
            titleLeftVList->add_back(new Label());
        }
        
        valueLeftVList = new VList;
        valueLeftVList->set_spacing(vshrink?5:11);
        valueLeftVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
        valueLeftVList->set_default_size(vshrink?80:160, vshrink?17:35);

        titleValueLabel = new UntranslatedLabel(indexName, HALIGN_CENTER);
        ownerValueLabel = new UntranslatedLabel(isPersistent ? persIndex->getOwner() : "System");
        
        pre2Index = new UntranslatedLabel();
        pre1Index = new UntranslatedLabel();
        thisIndex = new UntranslatedLabel();
        post1Index = new UntranslatedLabel();
        post2Index = new UntranslatedLabel();

        valueLeftVList->add_back(titleValueLabel);
        if (!isReasignOnly) {        
            valueLeftVList->add_back(ownerValueLabel);
        }
        valueLeftVList->add_back(groupButton);
        if (!isReasignOnly) {        
            valueLeftVList->add_back(pre2Index);
            valueLeftVList->add_back(pre1Index);
            valueLeftVList->add_back(thisIndex);
            valueLeftVList->add_back(post1Index);
            valueLeftVList->add_back(post2Index);
        }

        VList * scrollVList = new VList;
        scrollVList->set_spacing(vshrink?6:12);
        scrollVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
        scrollVList->set_default_size(vshrink?15:30, vshrink?17:35);

        scrollUp = new ImageButton("ic-up", "ic-up1", this);
        scrollDown = new ImageButton("ic-down", "ic-down1", this);
        scrollVList->add_back(scrollUp);
        scrollVList->add_back(scrollDown);

        VList * metaVList = new VList;
        metaVList->set_spacing(vshrink?6:12);
        metaVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER);
        metaVList->set_default_size(vshrink?70:140, vshrink?17:35);
        
        if (isEditable)
            but_metadata = new StaticTextButton(N_("Edit Metadata"), this);
        else
            but_metadata = new Label();
        Label * sokoballLabel = new Label("Sokoball:", HALIGN_RIGHT);
        Label * releaseLabel = new Label(N_("Release:"), HALIGN_RIGHT);
        Label * revisionLabel = new Label(N_("Revision:"), HALIGN_RIGHT);
        Label * compatibilityLabel = new Label(N_("Compatibility:"), HALIGN_RIGHT);
        Label * defLocationLabel = new Label(N_("Default Location:"), HALIGN_RIGHT);
        Label * crossmodeLabel = new Label(N_("Level types:"), HALIGN_RIGHT);
        
        if (!isReasignOnly) {
            metaVList->add_back(but_metadata);
            metaVList->add_back(sokoballLabel);
            if (WizardMode) {
                metaVList->add_back(releaseLabel);
                metaVList->add_back(revisionLabel);
            } else {
                metaVList->add_back(new Label());
                metaVList->add_back(new Label());
            }
            metaVList->add_back(crossmodeLabel);
            if (WizardMode) {
                metaVList->add_back(compatibilityLabel);
            } else {
                metaVList->add_back(new Label());
            }
            metaVList->add_back(defLocationLabel);
            metaVList->add_back(new Label());
        }

        valueMetaVList = new VList;
        valueMetaVList->set_spacing(vshrink?6:12);
        valueMetaVList->set_alignment(HALIGN_CENTER, VALIGN_CENTER);
        valueMetaVList->set_default_size(vshrink?37:75, vshrink?17:35);
        Widget * levelmodeWidget;
        Widget * sokoballValueWidget;
        if (indexName.empty()){
            levelmode = new LevelmodeButton(false);
            levelmodeWidget = levelmode;
            sokoballValueButton = new SokoballButton(false);
            sokoballValueWidget = sokoballValueButton;
        } else {
            levelmodeWidget = new Image(isPersistent && !(persIndex->isCross()) ?
                    "ic-link_copy" : "ic-link");
            sokoballValueWidget = new Label(isPersistent && (persIndex->getPackPath().find("soko/") == 0) ? "yes" : "no");
        }
        defLocationValueLabel = new Label(ecl::strf("%g", packIndex->getDefaultLocation()));
        releaseValueLabel = new Label(isPersistent ? ecl::strf("%d", persIndex->getRelease()) : "-");
        revisionValueLabel = new Label(isPersistent ? ecl::strf("%d", persIndex->getRevision()) : "-");
        compatibilityValueLabel = new Label(isPersistent ? ecl::strf("%.2f", persIndex->getCompatibility()) : "-");
        
        if (!isReasignOnly) {
            valueMetaVList->add_back(new Label());
            valueMetaVList->add_back(sokoballValueWidget);
            if (WizardMode) {
                valueMetaVList->add_back(releaseValueLabel);
                valueMetaVList->add_back(revisionValueLabel);
            } else {
                valueMetaVList->add_back(new Label());
                valueMetaVList->add_back(new Label());
            }
            valueMetaVList->add_back(levelmodeWidget);
            if (WizardMode) {
                valueMetaVList->add_back(compatibilityValueLabel);
            } else {
                valueMetaVList->add_back(new Label());
            }
            valueMetaVList->add_back(defLocationValueLabel);
            valueMetaVList->add_back(new Label());
        }
        

        if (vshrink) {
            if (isReasignOnly) {        
                this->add(titleLeftVList, Rect(vminfo.width/2 - 135, 7, 70, vminfo.height-48));
                this->add(valueLeftVList, Rect(vminfo.width/2 - 40, 7, 80, vminfo.height-48));
            } else {
                this->add(titleLeftVList, Rect(vminfo.width/2 - 150, 7, 70, vminfo.height-48));
                this->add(valueLeftVList, Rect(vminfo.width/2 - 70, 7, 80, vminfo.height-48));
                this->add(scrollVList, Rect(vminfo.width/2 + 15, 7+3*(17+6) + (vminfo.height-240)/2, 15, 5*17+4*6));
                this->add(metaVList, Rect(vminfo.width/2 + 40, 7, 70, vminfo.height-48));
                this->add(valueMetaVList, Rect(vminfo.width/2 + 117, 7, 37, vminfo.height-48));
            }
        } else {
            if (isReasignOnly) {        
                this->add(titleLeftVList, Rect(vminfo.width/2 - 270, 15, 140, vminfo.height-97));
                this->add(valueLeftVList, Rect(vminfo.width/2 - 80, 15, 160, vminfo.height-97));
            } else {
                this->add(titleLeftVList, Rect(vminfo.width/2 - 300, 15, 140, vminfo.height-97));
                this->add(valueLeftVList, Rect(vminfo.width/2 - 140, 15, 160, vminfo.height-97));
                this->add(scrollVList, Rect(vminfo.width/2 + 30, 15+3*(35+12) + (vminfo.height-480)/2, 30, 5*35+4*12));
                this->add(metaVList, Rect(vminfo.width/2 + 80, 15, 140, vminfo.height-97));
                this->add(valueMetaVList, Rect(vminfo.width/2 + 235, 15, 75, vminfo.height-97));
            }
        }
    
        errorLabel = new Label("", HALIGN_CENTER);
        this->add(errorLabel, Rect(vshrink?5:10, vminfo.height-(vshrink?48:97), vminfo.width-(vshrink?10:20), vshrink?17:35));
        
        if (isReasignOnly) 
            errorLabel->set_text(N_("Please reassign levelpack to another group for group deletion"));
       
        // Create buttons - positioning identical to Levelmenu
        but_edit = new StaticTextButton(N_("Compose Pack"), this);
        if (isPersistent && persIndex->isUpdatable() && persIndex->isCross()) {
            but_update = new StaticTextButton(N_("Update Pack"), this);
        } else {
            but_update = new Label();
        }
        but_ignore = new StaticTextButton(N_("Undo"), this);
        but_back = new StaticTextButton(N_("Ok"), this);
        
        HList * commandHList = new HList;
        commandHList->set_spacing(vshrink?5:10);
        commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP);
        commandHList->set_default_size(vshrink?70:140, vshrink?17:35);
        if (isReasignOnly) {
            commandHList->add_back(new Label());
            commandHList->add_back(new Label());
        } else {
            commandHList->add_back(but_edit);
            commandHList->add_back(but_update);
        }
        commandHList->add_back(but_ignore);
        commandHList->add_back(but_back);
        this->add(commandHList, Rect(vshrink?5:10, vminfo.height-(vshrink?25:50), vminfo.width-(vshrink?10:20), vshrink?17:35));
        
        updateLocationList();
        if (indexName.empty())
            // new levelpack
            switchToMetadataEdit();
    }
    
    void LevelPackConfig::updateLocationList() {
        pre2Index->set_text((position > 1) ? locationList[position - 2] : "");
        pre1Index->set_text((position > 0) ? locationList[position - 1] : "");
        thisIndex->set_text(didEditMetaData ? titleTF->getText() : packIndex->getName());
        post1Index->set_text((position < (int)locationList.size()-1) ? locationList[position + 1] : "");
        post2Index->set_text((position < (int)locationList.size()-2) ? locationList[position + 2] : "");        
    }
    
    void LevelPackConfig::switchToMetadataEdit() {
        if (!didEditMetaData) {
            didEditMetaData = true;
            titleTF = new TextField(titleValueLabel->getText(), this); 
            valueLeftVList->exchange_child(titleValueLabel, titleTF);
            delete titleValueLabel;
            ownerTF = new TextField(ownerValueLabel->getText()); 
            valueLeftVList->exchange_child(ownerValueLabel, ownerTF);
            delete ownerValueLabel;
            defLocationTF = new TextField(defLocationValueLabel->getText()); 
            valueMetaVList->exchange_child(defLocationValueLabel, defLocationTF);
            delete defLocationValueLabel;
            if (WizardMode) {
                releaseTF = new TextField(releaseValueLabel->getText()); 
                valueMetaVList->exchange_child(releaseValueLabel, releaseTF);
                delete releaseValueLabel;
                revisionTF = new TextField(revisionValueLabel->getText()); 
                valueMetaVList->exchange_child(revisionValueLabel, revisionTF);
                delete revisionValueLabel;
                compatibilityTF = new TextField(compatibilityValueLabel->getText()); 
                valueMetaVList->exchange_child(compatibilityValueLabel, compatibilityTF);
                delete compatibilityValueLabel;
            }
        }
    }
    
    bool LevelPackConfig::isUndoQuit() {
        return undo_quit;
    }
    
    bool LevelPackConfig::doChanges() {
        // change metadata
        if (didEditMetaData) {
            // the Index is persistent, user editabel and the user did switch to edit mode
            bool needSave = false;
            bool isNewIndex = persIndex->getName().empty();
            
            // check for valid input 
            // title
            std::string newtitle = titleTF->getText();
            std::string::size_type lastChar = newtitle.find_last_not_of(" ");
            if (lastChar == std::string::npos) {
                // the title is effectively an empty string
                errorLabel->set_text(N_("Error: empty title not allowed - press \"Undo\" to exit without modifications"));
                return false;
            }   
            // strip off trailing and leading spaces
            newtitle = newtitle.substr(0 , lastChar + 1);
            newtitle = newtitle.substr(newtitle.find_first_not_of(" "));
            if (newtitle != persIndex->getName()) {
                bool isSokoball = persIndex->getPackPath().find("soko/");
                if (isNewIndex) {
                    // check for filename usability of title
                    const std::string validChars("_- .#0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
                    if (newtitle.find_first_not_of(validChars, 0) != std::string::npos ||
                            (newtitle.length() >= 1 && newtitle[0] == '.')) {
                        errorLabel->set_text(N_("Error: use only \"a-zA-Z0-9 _-#\" for levelpack title"));
                        return false;               
                    }
                    
                    // set packPath to cross if link only
                    if (levelmode->isLinkOnly())
                        persIndex->markNewAsCross();
                    
                    isSokoball = sokoballValueButton->isSokoball();
                }
                if (!persIndex->setName(newtitle, isSokoball)) {
                    errorLabel->set_text(N_("Error: title already in use - choose another title"));
                    return false;
                }
                needSave = true;                
            }

            // check rest for need of save
            if (ownerTF->getText() != persIndex->getOwner()) {
                persIndex->setOwner(ownerTF->getText());
                needSave = true;
            }
            if (defLocationTF->getText() != ecl::strf("%g", packIndex->getDefaultLocation())) {
                double d = 0;
                // check value - keep old value on error 
                if ((sscanf(defLocationTF->getText().c_str(),"%lg", &d) == 1) &&
                        d > 0) {
                    packIndex->setDefaultLocation(d);
                    needSave = true;
                }
            }
            
            if (WizardMode) {
                if (releaseTF->getText() != ecl::strf("%d", persIndex->getRelease())) {
                    int i = 0;
                    // check value - keep old value on error 
                    if ((sscanf(releaseTF->getText().c_str(),"%d", &i) == 1) &&
                            i > 0) {
                        persIndex->setRelease(i);
                        needSave = true;
                    }
                }
                if (revisionTF->getText() != ecl::strf("%d", persIndex->getRevision())) {
                    int i = 0;
                    // check value - keep old value on error 
                    if ((sscanf(revisionTF->getText().c_str(),"%d", &i) == 1) &&
                            i > 0) {
                        persIndex->setRevision(i);
                        needSave = true;
                    }
                }
                if (compatibilityTF->getText() != ecl::strf("%.2f", persIndex->getCompatibility())) {
                    double d = 0;
                    // check value - keep old value on error 
                    if ((sscanf(compatibilityTF->getText().c_str(),"%lg", &d) == 1) &&
                            d >= 1) {
                        persIndex->setCompatibility(d);
                        needSave = true;
                    }
                }
            }
            
            // save
            if (needSave)
                persIndex->save();
            if (isNewIndex) {
                lev::Index::registerIndex(persIndex);
                lev::Index::setCurrentIndex(persIndex->getName());
            }
        }
        // regroup
        if (groupButton->get_value() != intialGroupPosition) {
            std::string newGroupName = groupButton->get_text(groupButton->get_value());
            // strip off square brackets used to mark default and pseudo groups
            if (newGroupName.size() > 2 && newGroupName[0] == '[' && 
                    newGroupName[newGroupName.size() -1] == ']') {
                newGroupName = newGroupName.substr(1, newGroupName.size() - 2);
            }
            packIndex->moveToGroup(newGroupName);
        } else if (isReasignOnly) {
            // the user did not reasign - take as an undo request
            undo_quit = true;
        }
        // relocate
        if (position != oldPosition)
            packIndex->locateBehind(position > 0 ? locationList[position - 1] : "");
        
        return true;
    }
    
    void LevelPackConfig::on_action(Widget *w) {
        if (w == but_back) {
            if (doChanges())
                Menu::quit();
            else
                invalidate_all();
        } else if (w == but_ignore) {
            if (packIndex->getName().empty()) {
                delete packIndex;
            }
            undo_quit = true;
            Menu::quit();
        } else if (w == but_update) {
            if (isPersistent && doChanges()) {
                persIndex->load(false, true);
                persIndex->save(true);
                Menu::quit();
            }
            invalidate_all();
        } else if (w == but_edit) {
            if (doChanges()) {
                LevelPackComposer m(isEditable);
                m.manage();
                Menu::quit();
            } else {
                invalidate_all();
            }
        } else if (w == scrollUp) {
            if (position > 0) {
                std::string tmp = locationList[position];
                locationList[position] = locationList[position - 1];
                locationList[position - 1] = tmp;
                position--;
                updateLocationList();
                invalidate_all();
            }
        } else if (w == scrollDown) {
            if (position < (int) locationList.size() - 1) {
                std::string tmp = locationList[position];
                locationList[position] = locationList[position + 1];
                locationList[position + 1] = tmp;
                position++;
                updateLocationList();
                invalidate_all();
            }
        } else if (w == but_metadata && !didEditMetaData) {
            switchToMetadataEdit();
            invalidate_all();
        } else if (w == titleTF && w != NULL) {
            thisIndex->set_text(titleTF->getText());
        }
    }
    
    void LevelPackConfig::draw_background(ecl::GC &gc) {
        const VMInfo *vminfo = video_engine->GetInfo();

        set_caption(("Enigma - Level Pack Configuration"));
        blit(gc, vminfo->mbg_offsetx, vminfo->mbg_offsety, enigma::GetImage("menu_bg", ".jpg"));
    }
    

}} // namespace enigma::gui
