#       Licensed to the Apache Software Foundation (ASF) under one
#       or more contributor license agreements.  See the NOTICE file
#       distributed with this work for additional information
#       regarding copyright ownership.  The ASF licenses this file
#       to you under the Apache License, Version 2.0 (the
#       "License"); you may not use this file except in compliance
#       with the License.  You may obtain a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#       Unless required by applicable law or agreed to in writing,
#       software distributed under the License is distributed on an
#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
#       KIND, either express or implied.  See the License for the
#       specific language governing permissions and limitations
#       under the License.


from urllib.parse import urlparse

from tg import tmpl_context as c
from tg import request, url
import json
import logging

import paginate

import ew as ew_core
import ew.jinja2_ew as ew

from allura.lib import validators as v


log = logging.getLogger(__name__)


def onready(text):
    return ew.JSScript('$(function () {%s});' % text)


class LabelList(v.UnicodeString):

    def __init__(self, *args, **kwargs):
        kwargs.setdefault('if_empty', [])
        super().__init__(*args, **kwargs)

    def _convert_to_python(self, value, state):
        value = super()._convert_to_python(value, state)
        return value.split(',')

    def _convert_from_python(self, value, state):
        value = ','.join(value)
        value = super()._convert_from_python(value, state)
        return value


class LabelEdit(ew.InputField):
    template = 'jinja:allura:templates/widgets/label_edit.html'
    validator = LabelList(if_empty=[])
    defaults = dict(
        ew.InputField.defaults,
        name=None,
        value=None,
        className='',
        show_label=True,
        placeholder=None)

    def from_python(self, value, state=None):
        if isinstance(value, str):
            return value
        elif value is None:
            return ''
        else:
            return ','.join(value)

    def resources(self):
        yield ew.JSLink('allura/js/jquery-ui.min.js', location='body_top_js')
        yield ew.JSLink('js/jquery.tagsinput.js')
        yield ew.CSSLink('css/jquery.tagsinput.css')
        yield onready('''
          $('input.label_edit').tagsInput({
              'height':'100%%',
              'width':'100%%',
              'autocomplete_url':'%(url)stags'
          });
          $('form').on('blur', '.ui-autocomplete-input', function() {
              setTimeout(function(){
                  var clicked = $(document.activeElement); // This is the element that has focus
                  if (clicked.is('#ui-active-menuitem')) {
                      return false;
                  } else {
                      var value = $('div.tagsinput div input').val();
                      var exists = $('input.label_edit').tagExist(value);
                      var default_value = $('div.tagsinput div input').attr('data-default');
                      if ((value !== default_value) && (!exists) && value !== '') {
                          $('input.label_edit').addTag(value);
                      }
                      $('input[type=submit]', this).prop('disabled', true);
                  }
                }, 1);
            });
        ''' % dict(url=c.app.url))


class ProjectUserCombo(ew.SingleSelectField):
    template = 'jinja:allura:templates/widgets/project_user_combo.html'

    # No options for widget initially.
    # It'll be populated later via ajax call.
    options = []

    def to_python(self, value, state):
        # Skipping validation, 'cause widget has no values initially.
        # All values loaded later via ajax.
        return value

    def resources(self):
        yield from super().resources()
        yield ew.JSLink('allura/js/jquery-ui.min.js', location='body_top_js')
        yield ew.CSSLink('css/autocomplete.css')  # customized in [6b78ed] so we can't just use jquery-ui.min.css
        yield ew.CSSLink('css/combobox.css')
        yield ew.JSLink('js/combobox.js')
        yield onready('''
          $('select.project-user-combobox').combobox({
            source_url: "%susers"
          });''' % c.project.url())


class NeighborhoodProjectSelect(ew.InputField):
    template = 'jinja:allura:templates/widgets/neighborhood_project_select.html'
    defaults = dict(
        ew.InputField.defaults,
        name=None,
        value=None,
        show_label=True,
        className=None)

    def __init__(self, url, **kw):
        super().__init__(**kw)
        if not isinstance(self.value, list):
            self.value = [self.value]
        self.url = url

    def from_python(self, value, state=None):
        return value

    def resources(self):
        yield from super().resources()
        yield ew.JSLink('allura/js/jquery-ui.min.js', location='body_top_js')
        yield ew.CSSLink('css/autocomplete.css')  # customized in [6b78ed] so we can't just use jquery-ui.min.css
        yield onready('''
          $('input.neighborhood-project-select').autocomplete({
            source: function (request, response) {
              $.ajax({
                url: "%s",
                dataType: "json",
                data: {
                  term: request.term
                },
                success: function (data) {
                  response(data.projects);
                }
              });
            },
            minLength: 3
          });''' % self.url)


class AttachmentList(ew_core.Widget):
    template = 'jinja:allura:templates/widgets/attachment_list.html'
    defaults = dict(
        ew_core.Widget.defaults,
        attachments=None,
        edit_mode=None)


class AttachmentAdd(ew_core.Widget):
    template = 'jinja:allura:templates/widgets/attachment_add.html'
    defaults = dict(
        ew_core.Widget.defaults,
        action=None,
        name=None)

    def resources(self):
        yield from super().resources()
        yield onready('''
            $(".attachment_form_add_button").click(function (evt) {
                $(this).hide();
                $(".attachment_form_fields", this.parentNode).show();
                evt.preventDefault();
            });
         ''')


class SubmitButton(ew.SubmitButton):
    attrs = {'class': 'ui-state-default ui-button ui-button-text'}


class Radio(ew.InputField):
    template = ew_core.render.Snippet('''<input {% if value %} checked{% endif %} {{widget.j2_attrs({
        'id':id,
        'type':field_type,
        'name':rendered_name,
        'class':css_class,
        'readonly':readonly,
        'value':value},
        attrs)}}>''', 'jinja2')
    defaults = dict(
        ew.InputField.defaults,
        field_type='radio')


class AutoResizeTextarea(ew.TextArea):
    defaults = dict(
        ew.TextArea.defaults,
        name=None,
        value=None,
        css_class='auto_resize')

    def resources(self):
        yield ew.JSLink('js/jquery.autosize-min.js')
        yield onready('''
            $('textarea.auto_resize').focus(function(){$(this).autosize();});
        ''')


class MarkdownEdit(ew.TextArea):
    template = 'jinja:allura:templates/widgets/markdown_edit.html'
    validator = v.UnicodeString()
    defaults = dict(
        ew.TextArea.defaults,
        name=None,
        value=None,
        show_label=True)

    def from_python(self, value, state=None):
        return value

    def resources(self):
        yield from super().resources()
        yield ew.JSLink('js/jquery.lightbox_me.js')
        yield ew.CSSLink('css/easymde.min.css', compress=False)
        yield ew.CSSLink('css/markitup_sf.css')
        yield ew.CSSLink('css/show-hint.css')
        yield ew.JSLink('js/easymde.min.js')
        yield ew.JSLink('js/sf_markitup.js')
        yield ew.JSLink('js/show-hint.js')
        yield ew.JSLink('js/usermentions-helper.js')
        yield onready('getProjectUsers(\'%s/users\')' % c.project.url())


class PageList(ew_core.Widget):
    template = 'jinja:allura:templates/widgets/page_list.html'
    defaults = dict(
        ew_core.Widget.defaults,
        name=None,
        limit=None,
        count=0,
        page=0,
        show_label=True,
        show_if_single_page=False,
        force_next=False)

    def paginator(self, count, page, limit, zero_based_pages=True):
        page_offset = 1 if zero_based_pages else 0
        limit = 10 if limit is None else limit

        def page_url(page):
            params = request.GET.copy()
            params['page'] = page - page_offset
            if params['page'] == 0:
                del params['page']
            # REQUEST_URI keeps double-slashes, but not available in all environments (like tests)
            curr_path = urlparse(request.environ.get('REQUEST_URI')).path or request.path
            return url(curr_path, params)
        return paginate.Page(list(range(count)), page + page_offset, int(limit),
                             url_maker=page_url,
                             )

    def prepare_context(self, context):
        context = super().prepare_context(context)
        count = context['count']
        page = context['page']
        limit = context['limit']
        context['paginator'] = self.paginator(count, page, limit)
        if context['force_next']:
            context['paginator'].next_page = context['paginator'].page + 1
        return context

    def resources(self):
        yield ew.CSSLink('css/page_list.css')

    @property
    def url_params(self, **kw):
        url_params = dict()
        for k, val in request.params.items():
            if k not in ['limit', 'count', 'page']:
                url_params[k] = val
        return url_params


class PageSize(ew_core.Widget):
    template = 'jinja:allura:templates/widgets/page_size.html'
    defaults = dict(
        ew_core.Widget.defaults,
        limit=None,
        name=None,
        count=0,
        show_label=False)

    @property
    def url_params(self, **kw):
        url_params = dict()
        for k, val in request.params.items():
            if k not in ['limit', 'count', 'page']:
                url_params[k] = val
        return url_params

    def resources(self):
        yield onready('''
            $('select.results_per_page').change(function () {
                this.form.submit();});''')


class JQueryMixin:
    js_widget_name = None
    js_plugin_file = None
    js_params = [
        'container_cls'
    ]
    defaults = dict(
        container_cls='container')

    def resources(self):
        yield from super().resources()
        if self.js_plugin_file is not None:
            yield self.js_plugin_file
        opts = {
            k: getattr(self, k)
            for k in self.js_params}
        yield onready('''
$(document).on('clone', function () {{
    $('.{}').{}({}); }});
$(document).trigger('clone');
            '''.format(self.container_cls, self.js_widget_name, json.dumps(opts)))


class SortableRepeatedMixin(JQueryMixin):
    js_widget_name = 'SortableRepeatedField'
    js_plugin_file = ew.JSLink('js/sortable_repeated_field.js')
    js_params = JQueryMixin.js_params + [
        'field_cls',
        'flist_cls',
        'stub_cls',
        'msg_cls',
        'append_to',
        'extra_field_on_focus_name',
    ]
    defaults = dict(
        container_cls='sortable-repeated-field',
        field_cls='sortable-field',
        flist_cls='sortable-field-list',
        stub_cls='sortable-field-stub',
        msg_cls='sortable-field-message',
        append_to='top',
        empty_msg='No fields have been defined',
        nonempty_msg='Drag and drop the fields to reorder',
        show_msg=True,
        show_button=True,
        extra_field_on_focus_name=None,
        repetitions=0)

    button = ew.InputField(
        css_class='add', field_type='button', value='New Field')


class SortableRepeatedField(SortableRepeatedMixin, ew.RepeatedField):
    template = 'jinja:allura:templates/widgets/sortable_repeated_field.html'
    defaults = dict(
        ew.RepeatedField.defaults,
        **SortableRepeatedMixin.defaults)


class SortableTable(SortableRepeatedMixin, ew.TableField):
    template = 'jinja:allura:templates/widgets/sortable_table.html'
    defaults = dict(
        ew.TableField.defaults,
        **SortableRepeatedMixin.defaults)


class StateField(JQueryMixin, ew.CompoundField):
    template = 'jinja:allura:templates/widgets/state_field.html'
    js_widget_name = 'StateField'
    js_plugin_file = ew.JSLink('js/state_field.js')
    js_params = JQueryMixin.js_params + [
        'selector_cls',
        'field_cls',
    ]
    defaults = dict(
        ew.CompoundField.defaults,
        js_params=js_params,
        container_cls='state-field-container',
        selector_cls='state-field-selector',
        field_cls='state-field',
        show_label=False,
        selector=None,
        states={},
    )

    @property
    def fields(self):
        return [self.selector] + list(self.states.values())


class DateField(JQueryMixin, ew.TextField):
    js_widget_name = 'datepicker'
    js_params = JQueryMixin.js_params
    container_cls = 'ui-date-field'
    defaults = dict(
        ew.TextField.defaults,
        container_cls='ui-date-field',
        css_class='ui-date-field')

    def resources(self):
        yield from super().resources()
        yield ew.JSLink('allura/js/jquery-ui.min.js', location='body_top_js')
        yield ew.CSSLink('allura/css/smoothness/jquery-ui.min.css', compress=False)  # compress will also serve from a different location, breaking image refs


class FieldCluster(ew.CompoundField):
    template = 'jinja:allura:templates/widgets/field_cluster.html'


class AdminField(ew.InputField):

    '''Field with the correct layout/etc for an admin page'''
    template = 'jinja:allura:templates/widgets/admin_field.html'
    defaults = dict(
        ew.InputField.defaults,
        field=None,
        css_class=None,
        errors=None)

    def __init__(self, **kw):
        super().__init__(**kw)
        for p in self.field.get_params():
            setattr(self, p, getattr(self.field, p))

    def resources(self):
        yield from self.field.resources()


class Lightbox(ew_core.Widget):
    template = 'jinja:allura:templates/widgets/lightbox.html'
    defaults = dict(
        name=None,
        trigger=None,
        options='',
        content='',
        content_template=None)

    def resources(self):
        yield ew.JSLink('js/jquery.lightbox_me.js')
        yield onready('''
            var $lightbox = $('#lightbox_{}');
            $('body').on('click', '{}', function(e) {{
                e.preventDefault();
                $lightbox.lightbox_me({});
            }});
            $lightbox.on('click', '.close', function(e) {{
                e.preventDefault();
                $lightbox.trigger('close');
            }});
        '''.format(self.name, self.trigger, self.options))


class DisplayOnlyField(ew.HiddenField):

    '''
    Render a field as plain text, optionally with a hidden field to preserve the value.
    '''
    template = ew.Snippet('''{{ (text or value or attrs.value)|e }}
        {%- if with_hidden_input is none and name or with_hidden_input -%}
        <input {{
            widget.j2_attrs({
                'type':'hidden',
                'name':name,
                'value':value,
                'class':css_class}, attrs)
        }}>
        {%- endif %}''', 'jinja2')
    defaults = dict(
        ew.HiddenField.defaults,
        text=None,
        value=None,
        with_hidden_input=None)
