#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# This file is part of Karesansui.
#
# Copyright (C) 2009 HDE, Inc.
#
# 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.
#

import snack
import traceback
import sys
import os
import socket
import re
import time

import installer.install
from installer.trans import _, N_
from installer.const import *

class SnackUI(object):

    def __init__(self, opts):

        self.opts = opts
        self.screen = snack.SnackScreen()

        self.size   = snack._snack.size()
        self.width  = min(self.size[0]-10,100)
        self.height = self.size[1]-15

        self.wins = [
                     WelcomeWin,
                     AdminSettingWin,
                     DatabaseSettingWin,
                     ConfirmWin,
                     FinishWin,
                    ]

    def run(self):
        self.screen.drawRootText(0, 0, "Welcome to %s %s" % (TITLE,VERSION,))
        self.screen.drawRootText(self.size[0]-len(COPYRIGHT), 0, "%s" % (COPYRIGHT))

        page = 0
        while page < len(self.wins):
            self.page = page
            func = self.wins[page](self.screen, self, self.opts)
            ret = func()

            if ret in [OK_VALUE, YES_VALUE, F12_VALUE]:
                page = page + 1
            elif ret == BACK_VALUE:
                page = page - 1
            elif ret in [CANCEL_VALUE, NO_VALUE, EXIT_VALUE]:
                page = page + 1
                break

        self.cleanup()

        if page >= len(self.wins):
            if ret in [OK_VALUE, YES_VALUE, F12_VALUE]:
                print _("Done.")

                if self.opts.is_installed is not True:
                    from installer.utils import get_kss_url
                    print _("You can now login through following url. Your login credential is '%s'.\n%s") % (self.opts.mailaddr, " " + "\n ".join(get_kss_url()))
        else:
            installer.install.write_log(_("Aborted page [%s].") % self.wins[page-1].__name__)
            print _("Aborted.")

        installer.install.write_log(_("Finish time: %s") % time.ctime())
        print _("See '%s' for detail.") % self.opts.logfile

        return ret

    def cleanup(self):
        if self.screen:
            self.screen.finish()

    def __del__(self):
        self.cleanup()

class ProgressWin:
    def __init__(self, screen, width):
        self.screen = screen
        self.width = width
        self.scale = snack.Scale(int(width), 100)

    def pop(self):
        self.screen.popWindow()
        self.screen.refresh()
        del self.scale
        self.scale = None

    def set(self, amount):
        self.scale.set(amount)
        self.screen.refresh()

    def update(self, amount):
        self.set(amount)

    def end(self, amount):
        self.update(amount)

    def start(self, size=None, now=None, text=None):

        if size is None:
            size = 1

        width = 55
        if (len(text) > width):
            width = min(len(text), self.width)

        self.text = snack.TextboxReflowed(width, text)

        self.screen.refresh()
        g = snack.GridForm(self.screen, _("Progress"), 1, 2)
        g.add(self.text, 0, 0, (0, 0, 0, 1), anchorLeft=1)

        self.scale = snack.Scale(width, size)
        g.add(self.scale, 0, 1)

        g.draw()
        self.screen.refresh()

def progressWin(screen, width):
    return ProgressWin(screen, width)

class BaseWin:
    def __init__(self, screen, ui, opts):
        self.ui = ui
        self.opts = opts
        self.screen = screen
        size = snack._snack.size()

class WelcomeWin(BaseWin):
    def __init__(self, *kwargs):
        BaseWin.__init__(self, *kwargs)
        self.opts.rpmsdir = getattr(self.opts, 'rpmsdir', "")

    def __call__(self):
        current_page = "[%d/%d]" % (self.ui.page+1, len(self.ui.wins))

        if self.opts.action == FLAG_CREATE_ADM:
            welcome_msg = _("Welcome to Karesansui Database Initialization Wizard.")
        else:
            welcome_msg = _("Welcome to Karesansui Installation Program.")
        text = snack.TextboxReflowed(self.ui.width, welcome_msg)

        buttons = snack.ButtonBar(self.screen, [OK_BUTTON, CANCEL_BUTTON], compact=BUTTON_COMPACT)

        g = snack.GridFormHelp(self.screen, _("Welcome") + current_page, None, 1, 2)
        g.add(text, 0, 0, padding = (0, 0, 0, 1))
        g.add(buttons, 0, 1, growx = 1)

        button = buttons.buttonPressed(g.runOnce())

        if button is None:
            button = OK_VALUE

        return button

class AdminSettingWin(BaseWin):
    def __init__(self, *kwargs):
        BaseWin.__init__(self, *kwargs)
        self.opts.fqdn = getattr(self.opts, 'fqdn', socket.gethostname())
        self.opts.lang = getattr(self.opts, 'lang', os.environ["LANG"][0:5])
        self.opts.mailaddr = getattr(self.opts, 'mailaddr', "")
        self.opts.password1 = getattr(self.opts, 'password1', "")
        self.opts.password2 = getattr(self.opts, 'password2', "")
        if self.opts.lang == "C":
            self.opts.lang = "en_US"

    def __call__(self):
        current_page = "[%d/%d]" % (self.ui.page+1, len(self.ui.wins))

        label_hostname = snack.Label(_("Hostname"))
        hostname = snack.Entry(40, text=self.opts.fqdn, hidden=False)

        label_pass1 = snack.Label(_("Password"))
        pass1 = snack.Entry(40, text=self.opts.password1, hidden=False, password=True)
        label_pass2 = snack.Label(_("Password (again)"))
        pass2 = snack.Entry(40, text=self.opts.password2, hidden=False, password=True)
        label_addr = snack.Label(_("Mail address"))
        addr = snack.Entry(40, text=self.opts.mailaddr, hidden=False)

        sub_g = snack.Grid(2, 4)
        sub_g.setField(label_hostname, 0, 0, anchorLeft=1)
        sub_g.setField(hostname,       1, 0, anchorLeft=1, padding=(1, 0, 0, 0))

        sub_g.setField(label_pass1,    0, 1, anchorLeft=1, padding=(0, 1, 0, 0))
        sub_g.setField(pass1,          1, 1, anchorLeft=1, padding=(1, 1, 0, 0))
        sub_g.setField(label_pass2,    0, 2, anchorLeft=1, padding=(0, 0, 0, 0))
        sub_g.setField(pass2,          1, 2, anchorLeft=1, padding=(1, 0, 0, 0))

        sub_g.setField(label_addr,     0, 3, anchorLeft=1, padding=(0, 1, 0, 0))
        sub_g.setField(addr,           1, 3, anchorLeft=1, padding=(1, 1, 0, 0))

        label_lang = snack.Label(_("Language"))
        items = []
        langs = {"ja_JP":_("Japanese"), "en_US":_("English")}
        selected = None
        for name,desc in langs.iteritems():
            label = "%s - %s" % (name,desc)

            if name == self.opts.lang:
                selected = name

            if len(label) >= self.ui.width:
                label = "%s..." % label[:self.ui.width]

            items.append((label, name))

        lb = snack.Listbox(min(self.ui.height-2, len(items)), scroll=1, returnExit=1)
        for (label, key) in items:
            lb.append(label, key)
        if selected != None:
            lb.setCurrent(selected)

        lang_g = snack.Grid(2, 1)
        lang_g.setField(label_lang, 0, 0, anchorLeft=1)
        lang_g.setField(lb,         1, 0, anchorLeft=1, padding=(0, 0, 0, 0))

        buttons = snack.ButtonBar(self.screen, [OK_BUTTON, BACK_BUTTON, CANCEL_BUTTON], compact=BUTTON_COMPACT)

        g = snack.GridFormHelp(self.screen, _("Administrator setting") + current_page, None, 1, 3)
        g.add(sub_g,      0, 0, growx=1)
        g.add(lang_g,     0, 1, growx=1, padding = (0, 1, 0, 0))
        g.add(buttons,    0, 2, growx=1, padding = (0, 1, 0, 0))

        button = buttons.buttonPressed(g.runOnce())

        if button is None:
            button = OK_VALUE

        if button == OK_VALUE:
            fqdn = hostname.value()
            self.opts.fqdn = fqdn
            mailaddr = addr.value()
            self.opts.mailaddr = mailaddr
            self.opts.lang = lb.current()
            password1 = pass1.value()
            password2 = pass2.value()
            self.opts.password1 = password1
            self.opts.password2 = password2

            if fqdn == '':
                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("%s is empty") % _('Hostname'), [OK_BUTTON], self.ui.width)
                return -1

            fqdn_error_msg = ""
            if fqdn != "localhost" and not re.compile(r'.*\.').match(fqdn):
                fqdn_error_msg = _('%s must include at least one dot . character.') % (_('Hostname'),)

            if re.compile(r'(^\.|.*\.$|.*\.\.|.*-\.|.*\.-|^-|.*-$)', re.VERBOSE).match(fqdn):
                fqdn_error_msg = _('%s must be specified like %s.') % (_('Hostname'), "host.example.com",)

            invalid_fqdn_regex = "[^-a-zA-Z0-9\.]"
            m = re.compile(invalid_fqdn_regex).search(fqdn)
            if m:
                fqdn_error_msg = _('%s\nAvailable characters are %s') % (_('Hostname'),_('a-z A-Z 0-9 . -'))
            if fqdn_error_msg != "":
                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), fqdn_error_msg, [OK_BUTTON], self.ui.width)
                return -1

            if mailaddr == '':
                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("%s is empty") % _('Mail address'), [OK_BUTTON], self.ui.width)
                return -1

            regex = "^(?P<localpart>[^@]+)@(?P<domainpart>.+)?$"
            m = re.compile(regex).search(mailaddr)
            if not m:
                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("%s is in an invalid format.") % _('Mail address'), [OK_BUTTON], self.ui.width)
                return -1

            if password1 == '':
                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("%s is empty") % _('Password'), [OK_BUTTON], self.ui.width)
                return -1
            if password1 != password2:
                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("Passwords mismatch"), [OK_BUTTON], self.ui.width)
                return -1

        return button

class DatabaseSettingWin(BaseWin):
    def __init__(self, *kwargs):
        BaseWin.__init__(self, *kwargs)
        self.opts.dbbind = getattr(self.opts, 'dbbind', DEFAULT_KARESANSUI_DB_URI)
        default_dbinit = 0
        if self.opts.is_installed is not True:
            default_dbinit = 1
        self.opts.dbinit = getattr(self.opts, 'dbinit', default_dbinit)

    def __call__(self):
        current_page = "[%d/%d]" % (self.ui.page+1, len(self.ui.wins))

        label_dbbind = snack.Label(_("Database path"))
        dbbind = snack.Entry(50, text=self.opts.dbbind, hidden=False)
        label_example = snack.Label(_("Example"))
        text = snack.TextboxReflowed(self.ui.width - 20,
                 "%s\nmysql://taizo:passwd@localhost/karesansui?charset=utf8" % DEFAULT_KARESANSUI_DB_URI)
        sub_g = snack.Grid(2, 2)
        sub_g.setField(label_dbbind,  0, 0, anchorLeft=1, padding=(0, 1, 0, 0))
        sub_g.setField(dbbind,        1, 0, anchorLeft=1, padding=(1, 1, 0, 0))
        sub_g.setField(label_example, 0, 1, anchorLeft=1, padding=(0, 1, 0, 0))
        sub_g.setField(text,        1, 1, anchorLeft=1, padding=(1, 1, 0, 0))

        #cb = snack.Checkbox(_("Initialize database?"))
        cb = snack.Checkbox(_("Initialize database?"), self.opts.dbinit)

        buttons = snack.ButtonBar(self.screen, [OK_BUTTON, BACK_BUTTON, CANCEL_BUTTON], compact=BUTTON_COMPACT)

        g = snack.GridFormHelp(self.screen, _("Database setting") + current_page, None, 1, 3)
        g.add(sub_g,   0, 0)
        cnt = 1
        if self.opts.action != FLAG_CREATE_ADM:
            g.add(cb,      0, cnt, padding=(0, 1, 0, 0))
            cnt = cnt + 1
        g.add(buttons, 0, cnt, padding=(0, 1, 0, 0), growx=1)

        button = buttons.buttonPressed(g.runOnce())

        if button is None:
            button = OK_VALUE

        if button == OK_VALUE:
            self.opts.dbbind = dbbind.value()
            if self.opts.action == FLAG_CREATE_ADM:
                self.opts.dbinit = True
            else:
                self.opts.dbinit = cb.selected()

            if self.opts.dbbind == '':
                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("%s is empty") % _("Database path"), [OK_BUTTON], self.ui.width)
                return -1

            check = False
            for k,v in DATABASE_MODULES.iteritems():
                regex = "^%s://" % k
                if re.compile(regex).match(self.opts.dbbind):
                    check = True

            #import pdb; pdb.set_trace()
            try:
                for y in [x.strip() for x in DEFAULT_PYTHONPATH.split(':') if x]:
                    if (y in sys.path) is False: sys.path.insert(0, y)
                from sqlalchemy import create_engine
                from sqlalchemy.exc import OperationalError

                if self.opts.dbinit != 0:
                    from installer.database import is_connect, DatabaseScriptError
                    try:
                        is_connect(self.opts.dbbind)
                    except DatabaseScriptError, e:
                        ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("%s is not connectable") % _("Database path"), [OK_BUTTON], self.ui.width)
                        return -1
            except:
                pass

            if check is False:
                ret = snack.ButtonChoiceWindow(self.screen, _("ERROR"), _("%s is in an invalid format.") % _("Database path"), [OK_BUTTON], self.ui.width)
                return -1

        return button

class ConfirmWin(BaseWin):
    def __init__(self, *kwargs):
        BaseWin.__init__(self, *kwargs)

    def __call__(self):
        current_page = "[%d/%d]" % (self.ui.page+1, len(self.ui.wins))

        if self.opts.action == FLAG_CREATE_ADM:
            confirm_msg = _("Are you sure to proceed to database initialize?")
        else:
            confirm_msg = _("Are you sure to proceed to install?")

        ret = snack.ButtonChoiceWindow(self.screen, _("Confirm") + current_page, confirm_msg,
                [OK_BUTTON, BACK_BUTTON, CANCEL_BUTTON], self.ui.width)

        if ret is None:
            ret = OK_VALUE

        if ret in [OK_VALUE, F12_VALUE]:

            dargs = {}
            dargs["progress_callback"] = progressWin(self.screen, self.ui.width)

            #import pdb; pdb.set_trace()
            try:
                installer.install.run(self.opts, **dargs)
                ret = OK_VALUE
            except installer.install.InstallError, e:
                error_msg = _("An error occurred while setting up the installer.\n%s" % e.value)
                installer.install.write_log(error_msg)
                r = snack.ButtonChoiceWindow(self.screen,_("ERROR"), error_msg,
                        [EXIT_BUTTON], self.ui.width)
                ret = EXIT_VALUE

        return ret


class FinishWin(BaseWin):
    def __init__(self, *kwargs):
        BaseWin.__init__(self, *kwargs)
        self.opts.config = getattr(self.opts, 'config', DEFAULT_KARESANSUI_CONF)

    def __call__(self):
        current_page = "[%d/%d]" % (self.ui.page+1, len(self.ui.wins))

        if self.opts.action == FLAG_CREATE_ADM:
            confirm_msg = _("Congratulations, database initialization was finished.")
        else:
            confirm_msg = _("Congratulations, installation was finished.")
        confirm_msg += "\n" + _("Please modify config file %s as needed.") % self.opts.config
        confirm_msg += "\n" + _("You can check the environment for Karesansui by executing the following script:\n# %s") % os.path.dirname(sys.argv[0]) + "/karesansui-checkenv"

        return snack.ButtonChoiceWindow(self.screen,_("Finish") + current_page, confirm_msg,
                [OK_BUTTON], self.ui.width - 10)

