from django.http import HttpResponse, Http404
from django.template import loader, RequestContext, Template

import qry, fns
from tiote import sa


def table_options(opt_type, with_keys=True, ):
    '''
    Generates a textual represenation (not unicode) of div.table-options for jsified Tables.
    This html serves as a sort of toolbars (compulsory) to the operations on a jsified table.
    '''
    # opt_type = "users || tbls || data
    l = ['<div class="table-options">'] # unclosed tag
    ctrls = ['all', 'none']
    # selection html
    l.append('<p class="">') # unclosed tag
    if not with_keys:
        l.append('<span style="color:#888;">[No primary keys defined]</span>')
    else:
        l.append('<span>{0}</span>'.format("Columns:" if opt_type=='tbls' else "Select: "))
        for ctrl in ctrls:
            l.append('<a class="selector select_{0}">{1}</a>'.format(ctrl, ctrl.title()))
        # actions to be carried out on selected rows
        l.append("<span>With Selected: </span>")
        # action(ctrls) html
        if opt_type == 'user' or opt_type == 'data': ctrls = ('edit', 'delete',)
        elif opt_type == 'tbl': ctrls = ('empty', 'drop',)
        elif opt_type == 'db': ctrls = ('drop',)
        elif opt_type == 'seq': ctrls = ('drop', 'reset', )
        # elif opt_type == 'tbl_like': ctrls = ('edit', 'drop', ) 
        elif opt_type == 'tbl_like': ctrls = ('drop', ) # currently only drops are supported
        # build html handles for ctrls
        for ctrl in ctrls:
            l.append('<a class="doer needy_doer action_{0}">{1}</a>'.format(ctrl, ctrl.title()))
        # add a refresh link for opt_type 'data'
        if opt_type == 'data':
            l.append('<a class="doer action_refresh" style="margin-left:20px">Refresh</a>')

    l.append("</p></div>") # closing unopen tags
    return "".join(l)



def select_input(rows, desc=None, initial=None):
    '''
    Creates html select elements from the sequence 'rows', with an initial value of 
    ``initial`` if it is not None and some options description to the element if 'desc'
    is not none
    '''
    # build select's El attributes
    _l_attrib_str = []
    for k in desc.keys():
        _l_attrib_str.extend([" ", k, "='", desc[k], "'"])
    attrib_str = "".join(_l_attrib_str)
    # build select's options
    options_str = []
    for row in rows:
        opt_template, prefx = "<option value='{0}'{1}>{0}</option>", ''
        if initial is not None and initial == row[0]:
            prefx = " selected='selected'"
        options_str.append( opt_template.format(row[0], prefx ))
    # final select string
    return "<select{0}>{1}</select>".format(attrib_str, ''.join(options_str))


def generate_sidebar(request):
    ret_string = '' # store the string representation to be generated by this function
    static_addr = fns.render_template(request, '{{STATIC_URL}}') 
    conn_params = fns.get_conn_params(request, update_db=True)
    db_list = qry.common_query(conn_params, 'db_list', request.GET)['rows']

    # if sctn is 'home' give list of database
    # if sctn is overview give list of schema if postgresql or leave as above
    # if sctn is not 'home' or 'oveview' give a list of all the objects described by sctn
    if request.GET.get('sctn') == 'hm' or \
            (conn_params['dialect'] == 'mysql' and request.GET.get('sctn') == 'db'):
        li_list = []
        for db_row in db_list:
            sufx = "&schm=%s" % sa.get_default_schema(conn_params) if conn_params['dialect'] == 'postgresql' else ''
            a = "<a class='icon-database' href='#sctn=db&v=ov&db={0}{1}'>{0}</a>".format(db_row[0],sufx)
            active_li = ' class="active"' if request.GET.get('db') == db_row[0] else '' # this would mark an item for hightlighting 
                                                                                        # occurs in the 'db' section of MySQL dialect
                                                                                        # and it selects the currently displayed db
            li_list.append('<li{1}>{0}</li>'.format(a, active_li)) 
        ret_string += '<h6>Databases</h6><ul>' + ''.join(li_list) + '</ul>'

    elif request.GET.get('sctn') == 'db':
        # decide on what to do
        _dict = {'db': request.GET.get('db')}
        # get a dropdown of databases from db_list with its initial set to what is in request.GET
        db_selection_form = select_input(db_list, desc={'id':'db_select'}, initial= _dict['db'])
        schema_list = qry.common_query(conn_params, 'schema_list', request.GET)['rows']
        _list = []
        for schm_row in schema_list:
            a = '<a class="icon-schema schm-link" href="#">%s</a>' % schm_row[0]
            # decide selected schema link
            li_pfx = " class='active'" if request.GET.get('schm', sa.get_default_schema(conn_params) ) == schm_row[0] else ''
            # append whole li element
            _list.append("<li{0}>{1}</li>".format(li_pfx, a))
            
        placeholder = '<h6 class="placeholder">%ss:</h6>' % fns._abbr['schm']
        sfx = "<ul>{0}</ul>".format( ''.join(_list) )

        ret_string = '<h6><a class="icon-back" href="#sctn=hm&v=hm">back home</a></h6>\
<h6>quick nav:</h6><div class="sidebar-item"><img src="{3}/tt_img/databases.png" />{0}</div>{1}{2}'.format( 
            db_selection_form, placeholder, sfx, static_addr
        )

    elif request.GET.get('sctn') in ('tbl', 'seq', 'views') :
        _dict = {'db': request.GET.get('db')}
        # get a dropdown of databases from db_list with its initial set to what is in request.GET
        db_selection_form = select_input(db_list, desc={'id':'db_select'}, initial= _dict['db'])

        s = ''
        if conn_params['dialect'] == 'postgresql':
            _dict['schm'] = request.GET.get('schm')
            # append schema selection with default to public
            schema_list = qry.common_query(conn_params, 'schema_list', request.GET)['rows']
            schema_selection_form = select_input(schema_list,desc={'id':'schema_select'},initial=_dict['schm'])
            # s = '<div class="sidebar-item"><img class="icon-schemas" />' + schema_selection_form + "</div>"
            s = '<div class="sidebar-item"><img src="{1}/tt_img/schemas.png" />{0}</div>'.format(
                schema_selection_form, static_addr)
        
        # table selection ul
        # table_list = qry.rpr_query(conn_params, 'existing_tables', fns.qd(request.GET))
        table_list = sa.get_table_names(conn_params, request.GET)
        table_list.sort() # noted that the list is sometimes not sorted from postgresql
        sfx_list = []
        pg_sfx = '&schm=' + _dict['schm'] if conn_params['dialect']=='postgresql' else ''
        for tbl_row in table_list:
            # decide selected table
            li_pfx = " class='active'" if request.GET.get('tbl') == tbl_row else ''
            a = '<a class="icon-table tbl-link" href="#">%s</a>' % tbl_row
            sfx_list.append("<li{0}>{1}</li>".format(li_pfx, a))
        
        # generate the string to be returned. It has the following order
        # 1. h6 saying 'quick navigation'
        # 2. db selection select element and schm selection select element if dialect is postgresql
        # 3. a h6.placeholder element shouting 'object types:'
        # 4. a ul having li > a each saying the specific 'object type' and linking to its appropriate view
        # ret_string = '<h6>quick nav:</h6><div><a class="james pull-right" href="">overview</a></div><br />\
        bck_lnk = '<h6><a class="icon-back" href="#sctn=db&v=ov&db={0}{1}">back to overview</a>'.format(
            request.GET.get('db'),
            '&schm=%s' % request.GET.get('schm') if request.GET.get('schm') else ''
            )
        ret_string = bck_lnk + '</h6>\
            <h6>quick nav:</h6><div class="sidebar-item"><img src="{url}/tt_img/databases.png" /> {db_select}</div>\
            {schema_select}{placeholder_html}{tbl_list}'.format( 
            db_select = db_selection_form, 
            schema_select = s,
            placeholder_html = '<h6 class="placeholder">%ss:</h6>' % fns._abbr[request.GET.get('sctn')], # 2
            tbl_list = "<ul>{0}</ul>".format( ''.join(sfx_list) ),
            url = static_addr
        )

#    return ret_string
    return HttpResponse(ret_string)



class HtmlTable():
    '''
    creates a html table from the given arguments
        - properties: a dict of table attributes
        - columns: an iterable containing the table heads
        - rows: and iterable containing some iterables
        - assoc_order: a list of numbers that specifies the order with which the columns and rows
                        and the rows should be ordered.
                        It could also be used to include and skip some rows and columns

    structure of return string (html)

    <div class='jsifyTable'>
        <div class='tbl-header'><table><tbody><tr></tr></tbody></table></div>
        <div class='tbl-body'>
            <table class='sql'><tbody>
                <tr><td><div class='data-entry'><code>{{data}}</code></div></td></tr>
            </tbody></table>
        </div>
    </div>
    '''

    def __init__(self, columns=[], rows=[], attribs={}, props={}, store={}, 
            assoc_order=None, static_addr = "",
            columns_desc=[], # displayed under the columns has help message
            **kwargs):
        self.props = props
        self.tbody_chldrn = []
        self.store_list = self._build_store_list(store)
        # store the keys in the table's markup
        self.keys_list = []
        if self.props.has_key('keys'):
            self.keys_list = self._build_keys_list(self.props['keys'])
            self.keys_column = [x[0] for x in self.props['keys']] # index 0 corresponds to column name
        
        # build attributes
        _attribs = {'class':'sql zebra-striped', 'id':'sql_table'}
        if hasattr(self, 'keys_column') and not len(self.keys_column):
            _attribs['class'] += ' no-primary-key'
        _attribs.update(attribs)
        if props.has_key('props_table') and props['props_table'] == True:
            _attribs['class'] += ' props-table'
        self.attribs_list = self._build_attribs_list(_attribs)
        # build <thead><tr> children
        self.columns_desc = columns_desc
        if columns is not None:
            self.columns = columns

            for _in in range(len(columns)):
                if len(columns_desc):
                # if there is a columns_desc list as a keyword argument to this function
                # append its contents to the columns by index (it has being formatted here)
                    # index 1 of each tuple in columns_desc holds the datatype of the table
                    columns[_in] = '<span class="column-name">%s</span><br /><span class="column-desc">%s</span>' %  (
                        columns[_in], columns_desc[_in][1])
                else:
                    columns[_in] = '<span class="column-name">%s</span>' % columns[_in]

            hd_list = ["<td class='controls'><div></div></td>"]

            # default order of columns is their index in the list 'columns'
            self.assoc_order = assoc_order if assoc_order else range(len(columns))
            # re arrange the order of columns
            for _i in self.assoc_order:
                hd_list.append('<td><div>%s</div></td>' % columns[_i])
        
            # empty td 
            hd_list.append('<td class="last-td"></td>')
            self.thead_chldrn = hd_list
        # build <tbody> children
        if rows is not None:
            [self.push(row, static_addr=static_addr) for row in rows]       

    def _build_attribs_list(self, attribs=None):
        attribs_list = []
        if attribs is not None:
            for k in attribs.keys():
                attribs_list.append(" {0}='{1}'".format(
                    str(k).lower(), str(attribs[k]) )
                )
        return attribs_list

    def _build_store_list(self, store):
        _l = []
        if store == {}: return _l
        for key in store.keys():
            _l.append("{0}:{1};".format(str(key), str(store[key])  ))
        return _l

    def _build_keys_list(self, keys):
        _l = []
        if _l is not []:
            for tup in keys:
                _l.append("{0}:{1};".format(tup[0], tup[len(tup) - 1]) )
        return _l

    def has_body(self):
        return len(self.tbody_chldrn) > 0

    def push(self, row, static_addr="", props=None):
        count = len(self.tbody_chldrn)
        row_list = ["<tr id='row_{0}'>".format(str(count))]
        l_cntrls = []
        if self.props is not None and self.props.has_key('keys') \
                and len(self.props['keys']) > 0 :
            # els a.checkers would be added for all tables with self.props['keys'] set
            l_cntrls.append("<input class='checker' id='check_{0}' type='checkbox' />".format(count))
            # go_link adds anchors in every row 
            # go_link_type determines the characteristics of the anchor: href || onclick
            if self.props.has_key('go_link') > 0 and self.props['go_link'] == True:
                l_cntrls.append( 
                    '<a href="{0}" class="go_link">{1}</a>'.format(
                        self.props['go_link_dest'] % row[0],
                        '<img src="{0}/tt_img/goto.png" />'.format(static_addr)
                    )
                )
            if self.props.keys().count('display_row') > 0 and self.props['display_row'] == True:
                l_cntrls.append(
                    u'<a class="go_link display_row pointer"><img src="{0}/tt_img/goto.png" /></a>'.format(static_addr)
                )
        tida = u'<td class="controls"{1}><div class="data-entry">{0}</div></td>'.format(
                u"".join(l_cntrls),
                u' style="min-width:%d%s' % (len(l_cntrls) * 18, 'px"') if len(l_cntrls) else ''
            )
        row_list.append(tida)

        _it = self.assoc_order if getattr(self, 'assoc_order', False) else range(len(row)) #
        for i in _it:
            row_i = unicode(row[i])
            # if this table has property props_table set to True skip truncation of very long row items
            if (self.props.has_key('props_table') and self.props['props_table']) \
                or \
            (hasattr(self, 'keys_column') and len(self.keys_column) == 0):
                column_data = row_i
            elif len(row_i) > 40 and hasattr(self, 'keys_column') and not self.keys_column.count(self.columns[i]):
                # trims the content until its off lenght 40 and adds a continuation placeholder
                if row_i.count('\n') and row_i.find('\n') < 40:
                    # if the newline is before the index 40 
                    column_data = row_i[ 0 : row_i.find('\n') ]
                else: column_data = row_i[0:40]
                column_data += '<span class="to-be-continued">...</span>'
            else:
                column_data = row_i
            column_data = column_data.replace(' ', '&nbsp;') # tds with spaces in them have its width set to its min-width
            row_list.append(u'<td><div class="data-entry"><code>{0}</code></div></td>'.format(str(column_data)))

        row_list.append(u'<td class="last-td"></td>')   # empty td
        row_list.append(u"</tr>")   # close tr tag
        self.tbody_chldrn.append(row_list)
    
    def to_element(self):
        thead = u'<div class="tbl-header"><table><tbody><tr{1}>{0}</tr></tbody></table></div>'.format(
            u''.join(self.thead_chldrn),
            ' class="has-column-desc"' if len(self.columns_desc) else ''
        )

        tbody = u'<div class="tbl-body"><table{0}{1}{2}><tbody>{3}</tbody></table></div>'.format(
            u''.join(self.attribs_list), # {0}
            u' data="' + ''.join(self.store_list) + u'"' if bool(self.store_list) else u'', #{1}
            u' keys="' + ''.join(self.keys_list) + u'"' if bool(self.keys_list) else u'' , #{2}
            u''.join([ ''.join(row) for row in self.tbody_chldrn])
        )
        
        return u'<div class="jsifyTable">' + thead + tbody + u'</div>'

    def __unicode__(self):
        return self.to_element()

