#This file is part of Tryton.  The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import gtk
import gobject
import gettext
import tryton.common as common
import tryton.rpc as rpc
import csv
from tryton.config import TRYTON_ICON, CONFIG
from tryton.exceptions import TrytonServerError

_ = gettext.gettext


class WinImport(object):
    "Window import"

    def __init__(self, model):
        self.parent = common.get_toplevel_window()
        self.dialog = gtk.Dialog(
                title=_("Import from CSV"), parent=self.parent,
                flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT
                | gtk.WIN_POS_CENTER_ON_PARENT)
        self.dialog.set_icon(TRYTON_ICON)

        dialog_vbox = gtk.VBox()
        hbox_mapping = gtk.HBox(True)
        dialog_vbox.pack_start(hbox_mapping, True, True, 0)

        frame_fields = gtk.Frame()
        frame_fields.set_shadow_type(gtk.SHADOW_NONE)
        viewport_fields = gtk.Viewport()
        scrolledwindow_fields = gtk.ScrolledWindow()
        scrolledwindow_fields.set_policy(gtk.POLICY_AUTOMATIC,
                gtk.POLICY_AUTOMATIC)
        viewport_fields.add(scrolledwindow_fields)
        frame_fields.add(viewport_fields)
        label_all_fields = gtk.Label(_("<b>All fields</b>"))
        label_all_fields.set_use_markup(True)
        frame_fields.set_label_widget(label_all_fields)
        hbox_mapping.pack_start(frame_fields, True, True, 0)

        vbox_buttons = gtk.VBox()
        vbox_buttons.set_border_width(5)
        hbox_mapping.pack_start(vbox_buttons, False, True, 0)

        button_add = gtk.Button(_("_Add"), stock=None, use_underline=True)
        button_add.set_alignment(0.0,0.0)
        img_button = gtk.Image()
        img_button.set_from_stock('tryton-list-add', gtk.ICON_SIZE_BUTTON)
        button_add.set_image(img_button)
        button_add.connect_after('clicked',  self.sig_sel)
        vbox_buttons.pack_start(button_add, False, False, 0)

        button_remove = gtk.Button(_("_Remove"), stock=None,
                use_underline=True)
        button_remove.set_alignment(0.0,0.0)
        img_button = gtk.Image()
        img_button.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_BUTTON)
        button_remove.set_image(img_button)
        button_remove.connect_after('clicked',  self.sig_unsel)
        vbox_buttons.pack_start(button_remove, False, False, 0)

        button_remove_all = gtk.Button(_("Clear"), stock=None,
                use_underline=True)
        button_remove_all.set_alignment(0.0,0.0)
        img_button = gtk.Image()
        img_button.set_from_stock('tryton-clear', gtk.ICON_SIZE_BUTTON)
        button_remove_all.set_image(img_button)
        button_remove_all.connect_after('clicked',  self.sig_unsel_all)
        vbox_buttons.pack_start(button_remove_all, False, False, 0)

        hseparator_buttons = gtk.HSeparator()
        vbox_buttons.pack_start(hseparator_buttons, False, False, 3)

        button_autodetect = gtk.Button(_("Auto-Detect"), stock=None,
                use_underline=True)
        button_autodetect.set_alignment(0.0,0.0)
        img_button = gtk.Image()
        img_button.set_from_stock('tryton-find', gtk.ICON_SIZE_BUTTON)
        button_autodetect.set_image(img_button)
        button_autodetect.connect_after('clicked',  self.sig_autodetect)
        vbox_buttons.pack_start(button_autodetect, False, False, 0)

        frame_import = gtk.Frame()
        frame_import.set_shadow_type(gtk.SHADOW_NONE)
        viewport_import = gtk.Viewport()
        scrolledwindow_import = gtk.ScrolledWindow()
        scrolledwindow_import.set_policy(gtk.POLICY_AUTOMATIC,
                gtk.POLICY_AUTOMATIC)
        viewport_import.add(scrolledwindow_import)
        frame_import.add(viewport_import)
        label_fields_import = gtk.Label(_("<b>Fields to import</b>"))
        label_fields_import.set_use_markup(True)
        frame_import.set_label_widget(label_fields_import)
        hbox_mapping.pack_start(frame_import, True, True, 0)

        frame_csv_param = gtk.Frame(None)
        frame_csv_param.set_shadow_type(gtk.SHADOW_ETCHED_OUT)
        dialog_vbox.pack_start(frame_csv_param, False, True, 0)
        alignment_csv_param = gtk.Alignment(0.5, 0.5, 1, 1)
        alignment_csv_param.set_padding(7, 7, 7, 7)
        frame_csv_param.add(alignment_csv_param)

        vbox_csv_param = gtk.VBox()
        alignment_csv_param.add(vbox_csv_param)
        hbox_csv_import = gtk.HBox()
        vbox_csv_param.pack_start(hbox_csv_import, False, True, 4)
        label_csv_import = gtk.Label(_("File to Import:"))
        hbox_csv_import.pack_start(label_csv_import, False, False, 0)
        self.import_csv_file = gtk.FileChooserButton(_("Open..."))
        hbox_csv_import.pack_start(self.import_csv_file, True, True, 0)

        expander_csv_import = gtk.Expander(None)
        expander_csv_import.set_expanded(True)
        vbox_csv_param.pack_start(expander_csv_import, False, True, 0)
        label_import_csv_param = gtk.Label(_("CSV Parameters"))
        expander_csv_import.set_label_widget(label_import_csv_param)
        table = gtk.Table(2, 4, False)
        table.set_border_width(8)
        table.set_row_spacings(9)
        table.set_col_spacings(8)
        expander_csv_import.add(table)

        label_import_csv_sep = gtk.Label(_("Field Separator:"))
        label_import_csv_sep.set_alignment(1, 0.5)
        table.attach(label_import_csv_sep, 0, 1, 0, 1)
        self.import_csv_sep = gtk.Entry()
        self.import_csv_sep.set_max_length(1)
        self.import_csv_sep.set_text(",")
        self.import_csv_sep.set_width_chars(1)
        table.attach(self.import_csv_sep, 1, 2, 0, 1)

        label_import_csv_del = gtk.Label(_("Text Delimiter:"))
        label_import_csv_del.set_alignment( 1, 0.5)
        table.attach(label_import_csv_del, 2, 3, 0, 1)
        self.import_csv_del = gtk.Entry()
        self.import_csv_del.set_text( "\"")
        self.import_csv_del.set_width_chars( 1)
        table.attach(self.import_csv_del, 3, 4, 0, 1)

        label_import_csv_enc = gtk.Label(_("Encoding:"))
        label_import_csv_enc.set_alignment( 1, 0.5)
        table.attach(label_import_csv_enc, 0, 1, 1, 2)
        self.import_csv_enc = gtk.combo_box_new_text()
        self.import_csv_enc.append_text("UTF-8")
        self.import_csv_enc.append_text("Latin1")
        cboxent_import_csv_enc = gtk.Entry()
        cboxent_import_csv_enc.unset_flags(gtk.CAN_FOCUS)
        self.import_csv_enc.add(cboxent_import_csv_enc)
        table.attach(self.import_csv_enc, 1, 2, 1, 2)

        label_import_csv_skip = gtk.Label(_("Lines to Skip:"))
        label_import_csv_skip.set_alignment( 1, 0.5)
        table.attach(label_import_csv_skip, 2, 3, 1, 2)

        self.import_csv_skip_adj = gtk.Adjustment(0, 0, 100, 1, 10)
        self.import_csv_skip = gtk.SpinButton(self.import_csv_skip_adj, 1, 0)
        table.attach(self.import_csv_skip, 3, 4, 1, 2)

        button_cancel = gtk.Button("gtk-cancel", stock="gtk-cancel")
        self.dialog.add_action_widget(button_cancel, gtk.RESPONSE_CANCEL)
        button_cancel.set_flags(gtk.CAN_DEFAULT)

        button_ok = gtk.Button("gtk-ok", stock="gtk-ok")
        self.dialog.add_action_widget(button_ok, gtk.RESPONSE_OK)
        button_ok.set_flags(gtk.CAN_DEFAULT)

        self.dialog.vbox.pack_start(dialog_vbox)

        self.model = model
        self.fields_data = {}

        self.dialog.show_all()
        self.import_csv_file.set_current_folder(CONFIG['client.default_path'])

        self.view1 = gtk.TreeView()
        self.view1.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
        self.view1.connect('row-expanded', self.on_row_expanded)
        scrolledwindow_fields.add(self.view1)
        self.view2 = gtk.TreeView()
        self.view2.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
        scrolledwindow_import.add(self.view2)
        self.view1.set_headers_visible(False)
        self.view2.set_headers_visible(False)

        cell = gtk.CellRendererText()
        column = gtk.TreeViewColumn(_('Field name'), cell, text=0,
                background=2)
        self.view1.append_column(column)

        cell = gtk.CellRendererText()
        column = gtk.TreeViewColumn(_('Field name'), cell, text=0)
        self.view2.append_column(column)

        self.model1 = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING,
                gobject.TYPE_STRING)
        self.model2 = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)

        self.fields = {}
        self.fields_invert = {}

        self.model_populate(self._get_fields(model))

        self.view1.set_model(self.model1)
        self.view2.set_model(self.model2)
        self.view1.show_all()
        self.view2.show_all()

    def model_populate(self, fields, parent_node=None, prefix_field='',
            prefix_name=''):
        fields_order = fields.keys()
        fields_order.sort(lambda x, y: -cmp(fields[x].get('string', ''),
                fields[y].get('string', '')))
        for field in fields_order:
            if not fields[field].get('readonly', False):
                self.fields_data[prefix_field + field] = fields[field]
                name = fields[field]['string'] or field
                node = self.model1.insert(parent_node, 0, [name, prefix_field +
                    field, (fields[field].get('required', False) and
                        common.COLORS['required']) or 'white'])
                name = prefix_name + name
                self.fields[prefix_field + field] = (name,
                        fields[field].get('relation'))
                self.fields_invert[name] = prefix_field + field
                if fields[field].get('relation'):
                    self.model1.insert(node, 0, [None, '', 'white'])

    def _get_fields(self, model):
        args = ('model', model, 'fields_get', None, rpc.CONTEXT)
        try:
            return rpc.execute(*args)
        except TrytonServerError, exception:
            return common.process_exception(exception, self.dialog, *args)

    def on_row_expanded(self, treeview, iter, path):
        child = self.model1.iter_children(iter)
        if self.model1.get_value(child, 0) is None:
            prefix_field = self.model1.get_value(iter, 1)
            name, model = self.fields[prefix_field]
            self.model_populate(self._get_fields(model), iter, prefix_field +
                    '/', name + '/')
            self.model1.remove(child)

    def sig_autodetect(self, widget=None):
        fname = self.import_csv_file.get_filename()
        if not fname:
            common.message(_('You must select an import file first!'),
                    self.dialog)
            return True
        csvsep = self.import_csv_sep.get_text()
        csvdel = self.import_csv_del.get_text()
        csvcode = self.import_csv_enc.get_active_text() \
                or 'UTF-8'

        self.import_csv_skip.set_value(1)
        try:
            data = csv.reader(open(fname, 'rb'), quotechar=csvdel,
                    delimiter=csvsep)
        except IOError:
            common.warning(_('Error opening CSV file'), self.dialog,
                    _('Error'))
            return True
        self.sig_unsel_all()
        word = ''
        for line in data:
            for word in line:
                word = word.decode(csvcode)
                if word not in self.fields_invert and word not in self.fields:
                    iter = self.model1.get_iter_first()
                    prefix = ''
                    prefix_name = ''
                    for parent in word.split('/')[:-1]:
                        while iter:
                            if self.model1.get_value(iter, 0) == parent or \
                                    self.model1.get_value(iter, 1) == \
                                    (prefix + parent):
                                self.on_row_expanded(self.view1, iter,
                                        self.model1.get_path(iter))
                                break
                            iter = self.model1.iter_next(iter)
                        prefix = parent + '/'
                if word in self.fields_invert:
                    name = word
                    field = self.fields_invert[word]
                elif word in self.fields:
                    name = self.fields[word][0]
                    field = word
                else:
                    common.warning(
                            _('Error processing the file at field %s.') %
                            word, self.dialog, _('Error'))
                    return True
                num = self.model2.append()
                self.model2.set(num, 0, name, 1, field)
            break
        return True

    def sig_sel(self, widget=None):
        sel = self.view1.get_selection()
        sel.selected_foreach(self._sig_sel_add)

    def _sig_sel_add(self, store, path, iter):
        num = self.model2.append()
        name = self.fields[store.get_value(iter, 1)][0]
        self.model2.set(num, 0, name, 1, store.get_value(iter,1))

    def sig_unsel(self, widget=None):
        store, paths = self.view2.get_selection().get_selected_rows()
        while paths:
            store.remove(store.get_iter(paths[0]))
            store, paths = self.view2.get_selection().get_selected_rows()

    def sig_unsel_all(self, widget=None):
        self.model2.clear()

    def run(self):
        button = self.dialog.run()
        if button == gtk.RESPONSE_OK:
            fields = []
            iter = self.model2.get_iter_root()
            while iter:
                fields.append(self.model2.get_value(iter, 1))
                iter = self.model2.iter_next(iter)

            csv_data = {
                'fname': self.import_csv_file.get_filename(),
                'sep': self.import_csv_sep.get_text(),
                'del': self.import_csv_del.get_text(),
                'skip': self.import_csv_skip.get_value(),
                'combo': self.import_csv_enc.get_active_text() or 'UTF-8'
            }
            self.parent.present()
            self.dialog.destroy()
            if csv_data['fname']:
                return self.import_csv(csv_data, fields, self.model)
            return False
        else:
            self.parent.present()
            self.dialog.destroy()
            return False

    def import_csv(self, csv_data, fields, model):
        # TODO: make it works with references
        fname = csv_data['fname']
        data = list(csv.reader(open(fname, 'rb'), quotechar=csv_data['del'],
            delimiter=csv_data['sep']))[int(csv_data['skip']):]
        datas = []

        for line in data:
            if not line:
                continue
            datas.append([x.decode(csv_data['combo']).encode('utf-8') \
                    for x in line])
        try:
            res = rpc.execute('model', model, 'import_data', fields, datas,
                    rpc.CONTEXT)
        except TrytonServerError, exception:
            common.process_exception(exception, self.dialog)
            return False
        if res[0] >= 0:
            if res[0] == 1:
                common.message(_('%d record imported!') % res[0])
            else:
                common.message(_('%d records imported!') % res[0])
        else:
            buf = ''
            for key, val in res[1].items():
                buf += ('\t%s: %s\n' % (str(key), str(val)))
            common.error(_('Importation Error!'),
                    _('Error trying to import this record:\n' \
                    '%s\nError Message:\n%s\n\n%s') % (buf, res[2], res[3]))
        return True
