from django import forms
from django.db.models.functions import Lower
from django.forms.utils import flatatt
from django.forms import ValidationError
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.core.urlresolvers import reverse_lazy
from django.contrib.staticfiles.templatetags.staticfiles import static
from crispy_forms.helper import FormHelper
import crispy_forms.layout as cfl
import crispy_forms.bootstrap as cfb
from bootstrap3_datetime.widgets import DateTimePicker
from .models import Set, EnvironmentMeasure, Bait, Equipment, SetTag, BenthicCategory
from ..trip.models import Trip
from ..habitat.models import Reef, ReefType, Substrate, SubstrateComplexity
from django.conf import settings
import json

# from ..bruv.views.customField import CustomField

US_WEST_AWS_S3 = 'https://s3-us-west-2.amazonaws.com/'

timepicker_opts = {"format": "HH:mm", "showClear": True}
datepicker_opts = {"format": "MMMM DD YYYY", "showClear": True, "extraFormats": ["D/M/Y"]}


class SetForm(forms.ModelForm):
    """
    Main set form
    """
    set_date = forms.DateField(
        input_formats=['%B %d %Y'],
        widget=DateTimePicker(options=datepicker_opts)
    )
    drop_time = forms.TimeField(
        input_formats=['%H:%M'],
        widget=DateTimePicker(options=timepicker_opts, icon_attrs={'class': 'glyphicon glyphicon-time'})
    )
    haul_date = forms.DateField(
        input_formats=['%B %d %Y'],
        widget=DateTimePicker(options=datepicker_opts),
        required=False
    )
    haul_time = forms.TimeField(
        input_formats=['%H:%M'],
        widget=DateTimePicker(options=timepicker_opts, icon_attrs={'class': 'glyphicon glyphicon-time'}),
        required=False
    )
    reef = forms.ModelChoiceField(
        queryset=Reef.objects.all()
    )
    habitat = forms.ModelChoiceField(
        queryset=ReefType.objects.all()
    )

    class Meta:
        model = Set
        fields = ['trip', 'set_date', 'haul_date', 'latitude', 'longitude', 'depth',
                  'drop_time', 'haul_time', 'reef', 'habitat', 'equipment', 'bait',
                  'reef_habitat', 'code']
        exclude = ('reef_habitat',)
        widgets = {
            'trip': forms.HiddenInput(),
        }

    def __init__(self, *args, **kwargs):
        try:
            trip_pk = kwargs.pop('trip_pk')
        except KeyError:
            trip_pk = False

        super().__init__(*args, **kwargs)

        if trip_pk:
            self.fields['reef'].queryset = Trip.objects.get(pk=trip_pk).location.reef_set

        self.helper = FormHelper(self)
        self.helper.form_tag = False
        self.fields['code'].label += '**'
        self.fields['latitude'].widget.attrs['step'] = 'any'
        self.fields['longitude'].widget.attrs['step'] = 'any'
        help_text = '<small class="help-block">*Required Field &nbsp;&nbsp;&nbsp; **Note: If code is left blank, ' \
                    'it will be automatically generated.</small>'
        self.helper.layout.append(cfl.Div(cfl.HTML(help_text)))


class SetSearchForm(forms.Form):
    """
    Set search form
    """
    search_set_date = forms.DateField(required=False,
                                      input_formats=['%B %d %Y'],
                                      widget=DateTimePicker(options=datepicker_opts))
    reef = forms.ModelChoiceField(required=False,
                                  queryset=Reef.objects.all().order_by(
                                      Lower('site__name'), Lower('name'), Lower('site__code'), Lower('code')
                                  ))
    habitat = forms.ModelChoiceField(required=False,
                                     queryset=ReefType.objects.all().order_by(Lower('type')))
    equipment = forms.ModelChoiceField(required=False,
                                       queryset=Equipment.objects.order_by(Lower('frame_type__type'),
                                                                           Lower('container__type'),
                                                                           Lower('camera'),
                                                                           'stereo')
                                       )
    bait = forms.ModelChoiceField(required=False,
                                  queryset=Bait.objects.filter(set__in=Set.objects.all())
                                  .distinct().order_by(Lower('type'), Lower('description'), 'oiled')
                                  )
    code = forms.CharField(required=False)

    def __init__(self, *args, **kwargs):
        trip_id = kwargs.pop('trip_id', False)

        super().__init__(*args, **kwargs)

        if trip_id:
            self.fields['reef'].queryset = Reef.objects.filter(id__in=Trip.objects.get(pk=trip_id).
                                                               set_set.all().values('reef_habitat__reef'))

        self.helper = FormHelper(self)
        self.helper.form_class = 'form-inline set-search form-group-sm'
        self.helper.form_method = "get"
        self.helper.layout = cfl.Layout(
            cfl.Div(
                'search_set_date',
                'reef',
                'habitat',
                'equipment',
                'bait',
                'code'),
            cfl.Div(
                cfb.FormActions(
                    cfl.HTML("""<a role="button" class="btn btn-default btn-fp cancel-button"
                    href="{% url "trip_set_list" trip_pk %}">Reset</a>"""),
                    cfl.Submit('', 'Search', css_class='btn-fp')),
                css_class='row pull-right'))


class EnvironmentMeasureForm(forms.ModelForm):
    """
    Environmental measurement form used as part of set form; used twice in form for drop and haul
    """
    dissolved_oxygen_measure = forms.ChoiceField(
        required=False,
        label='&nbsp;',
        choices=[('mgl', 'mg/L')]
    )

    class Meta:
        model = EnvironmentMeasure
        fields = ['water_temperature', 'salinity',
                  'conductivity', 'dissolved_oxygen', 'dissolved_oxygen_measure',
                  'tide_state', 'estimated_wind_speed', 'measured_wind_speed',
                  'wind_direction', 'cloud_cover', 'surface_chop']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper.form_tag = False
        self.fields['measured_wind_speed'].label += '**'
        help_text = '<small class="help-block">**Use kts only when you have instrumentation ' \
                    'that makes a precise recording.</small>'
        self.helper.layout.append(cfl.Div(cfl.HTML(help_text)))


class ImageSelectWidget(forms.FileInput):
    """
    Helper widget for image controls in set form
    """

    def __init__(self, image_url=None, attrs={}):
        self.image_url = image_url
        super().__init__(attrs)

    def render(self, name, value, attrs=None):
        template = '''
        <div class="image-select-widget-parent">
            <div class="image-select-widget" style="background-image:url({});">&nbsp;</div>
            <input type="file" value="{}" name="{}" />
            <div class="caption">{}</div>
        </div>
        '''
        output = format_html(template,
                             static('images/upload_image.png') if not self.image_url else self.image_url,
                             value,
                             name,
                             'Upload image' if not self.image_url else 'Choose another image')
        return mark_safe(output)


class BenthicWidget(forms.Widget):
    """
    Helper widget for benthic category data in set form
    """

    def render(self, name, value, attrs=None):
        try:
            total_percent = value.pop('total_percent', 0)
            left = ''
            center = ''
            right = ''
            root_substrates = BenthicCategory.objects.all()

            for sp in list(zip(value['substrates'], value['percents'])):
                left += '''
                <div class="substrate-row">
                    <select class="substrate select form-control" name="benthic-category">
                '''
                for s in root_substrates:
                    left += '<option value="{}"{}>{}</option>'.format(
                        s.pk,
                        ' selected="selected"' if sp[0].pk == s.pk else '',
                        (s.get_level() * '-' + ' ') + s.name)
                left += '''
                    </select>
                </div>
                '''

                center += '''
                <div class="substrate-row"><div class="input-holder">
                    <input class="percent" name="percent" type="number" step="1" min="1" max="100" value="{}" />
                </div></div>
                '''.format(sp[1])

                right += '''
                <div class="substrate-row">
                    <a href="#" class="split">Split=</a>
                    <a href="#" class="remove">Remove</a>
                </div>
                '''
        except (TypeError, AttributeError):
            total_percent = 0
            left = ''
            center = ''
            right = ''

        template = '''
        <div class="habitat-substrate-parent clear">
            <div class="left clear">
                {}
                <div class="substrate-row">
                    <button class="btn btn-primary btn-fp add-substrate">+</button>
                    <span class="total">Total</span>
                </div>
            </div>

            <div class="center">
                {}
                <div class="substrate-row">
                    <div class="input-holder">
                        <input name="total-percent" type="number" readonly="readonly" value="{}" />
                    </div>
                </div>
            </div>

            <div class="right">
                {}
                <div class="substrate-row">
                    <span class="help-text">Categories must total 100%</span>
                </div>
            </div>
        </div>
        '''
        return mark_safe(format_html(template, mark_safe(left), mark_safe(center), total_percent, mark_safe(right)))

    def value_from_datadict(self, data, files, name):
        return {
            'total_percent': int(data.get('total-percent')),
            'percents': [int(p) for p in data.getlist('percent')],
            'substrates': [int(s) for s in data.getlist('benthic-category')]
        }


class BenthicField(forms.Field):
    """
    Helper field for benthic data in set form
    """
    widget = BenthicWidget

    def to_python(self, value):
        if value is not None and 'substrates' in value and \
                len(value['substrates']) > 0 and isinstance(value['substrates'][0], int):
            value['substrates'] = [BenthicCategory.objects.get(pk=s_id) for s_id in value['substrates'][:]]
        return value

    def prepare_value(self, value):
        if hasattr(self, 'initial'):
            return self.initial

    def validate(self, value):
        super(BenthicField, self).validate(value)
        if value['total_percent'] != 100 and len(value['substrates']) > 0:
            raise ValidationError('Categories must total 100%', code='not_100')
        if len(value['substrates']) != len(set(value['substrates'])):
            raise ValidationError('Must not have duplicate categories', code='no_duplicates')


class SetLevelDataForm(forms.ModelForm):
    """
    Form for set level data used in set form
    """
    bruv_image_file = forms.FileField(required=False,
                                      widget=ImageSelectWidget,
                                      label='Habitat photo: BRUV')
    splendor_image_file = forms.FileField(required=False,
                                          widget=ImageSelectWidget,
                                          label='Habitat photo: splendor of the reef')
    benthic_category = BenthicField(required=False,
                                    label='Benthos Categories & Forms')

    class Meta:
        model = Set
        fields = ['visibility', 'current_flow_instrumented', 'current_flow_estimated',
                  'bruv_image_file', 'splendor_image_file', 'benthic_category', 'field_of_view',
                  'substrate_relief_mean', 'substrate_relief_sd']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper.form_tag = False
        s3_base_url = US_WEST_AWS_S3 + settings.HABITAT_IMAGE_BUCKET
        bruv_image_url = s3_base_url + kwargs['instance'].bruv_image_url \
            if 'instance' in kwargs and kwargs['instance'] and kwargs['instance'].bruv_image_url else None
        splendor_image_url = s3_base_url + kwargs['instance'].splendor_image_url \
            if 'instance' in kwargs and kwargs['instance'] and kwargs['instance'].splendor_image_url else None
        self.fields['bruv_image_file'].widget = ImageSelectWidget(image_url=bruv_image_url)
        self.fields['splendor_image_file'].widget = ImageSelectWidget(image_url=splendor_image_url)
        if 'instance' in kwargs and kwargs['instance'] and kwargs['instance'].benthiccategoryvalue_set:
            value = {
                'total_percent': 0,
                'percents': [],
                'substrates': []
            }
            for bcv in kwargs['instance'].benthiccategoryvalue_set.all():
                value['percents'].append(bcv.value)
                value['substrates'].append(bcv.benthic_category)
            value['total_percent'] = sum(value['percents'])
            self.fields['benthic_category'].initial = value
            self.fields['visibility'].required = False
        self.helper.layout = cfl.Layout(
            'visibility', 'current_flow_instrumented', 'current_flow_estimated',
            cfl.Div('bruv_image_file', 'splendor_image_file', 'benthic_category',
                    'field_of_view', 'substrate_relief_mean', 'substrate_relief_sd')
        )


class SelectizeWidget(forms.SelectMultiple):
    """
    Helper widget for selectize controls (autocomplete with multi-select)
    """
    template = '<select class="selectize" multiple="multiple"{}>'

    def render(self, name, value, attrs=None, choices=()):
        if value is None:
            value = []
        final_attrs = self.build_attrs(attrs, name=name)
        output = [format_html(self.template, flatatt(final_attrs))]
        options = self.render_options(choices)
        if options:
            output.append(options)
        output.append('</select>')
        return mark_safe('\n'.join(output))


class SetLevelCommentsForm(forms.ModelForm):
    """
    Form for set level comments used in set form
    """
    comments = forms.CharField(widget=forms.Textarea(attrs={'rows': 3}), required=False)
    tags = forms.MultipleChoiceField(widget=SelectizeWidget, choices=SetTag.get_choices, required=False)

    class Meta:
        model = Set
        fields = ['comments', 'tags']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper.form_tag = False


class SetBulkUploadForm(forms.Form):
    set_file = forms.FileField()

    def __init__(self, *args, **kwargs):
        trip_id = kwargs.pop('trip_id')
        super().__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper.form_action = reverse_lazy('set_bulk_upload', args=[trip_id])
        self.helper.form_class = 'form-inline'
        self.helper.layout = cfl.Layout(
            cfl.Field('set_file', css_class='form-control'),
        )
        self.helper.add_input(cfl.Submit('upload', 'Upload', css_class='btn-fp'))


class RemoveWidget(forms.Widget):
    """
    Helper widget for video form remove link
    """
    template = '<div class="sub-control"><a href="#" class="remove">Remove</a></div>'

    def value_from_datadict(self, data, files, name):
        return data.getlist(name, [0])

    def render(self, name, value, attrs=None):
        html = ''
        for _ in value:
            html += self.template
        return mark_safe(html)


class MultiRowCharField(forms.CharField):
    """
    Helper field for video form source and path fields
    """
    def to_python(self, value):
        return value


class MultiRowTextInput(forms.Widget):
    """
    Helper widget for video form source and path fields
    """
    template = '<div class="sub-control">' \
               '<input class="form-control textInput" name="{}" type="text" value="{}" {} />' \
               '</div>'

    def value_from_datadict(self, data, files, name):
        return list(None if datum == '' else datum for datum in data.getlist(name, [None]))

    def render(self, name, value, attrs=None):
        file = open('instace.txt', 'w')
        file.write(repr(value))
        file.close()
        html = ''
        for val in value:
            html += self.template.format(name, '' if val is None else val, flatatt(attrs))
        return mark_safe(html)


class SetCustomFieldsForm(forms.ModelForm):
    """
    Form for set custom fields in set form
    """
    jsonColumnName = MultiRowCharField(required=False, label='Custom Field Name', max_length=100, widget=MultiRowTextInput)
    jsonColumnValue = MultiRowCharField(required=False, label='Custom Value', max_length=100, widget=MultiRowTextInput)
    remove_row = forms.Field(required=False, label='', widget=RemoveWidget)

    class Meta:
        model = Set
        fields = ['jsonColumnName', 'jsonColumnValue', 'remove_row']

    def __init__(self, *args, **kwargs):
        super(SetCustomFieldsForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper.form_tag = False
        self.fields['jsonColumnName'].initial = [None]
        self.fields['jsonColumnValue'].initial = [None]
        self.fields['remove_row'].initial = [0]
        # when edit
        if 'instance' in kwargs and kwargs['instance'] and kwargs['instance'].custom_field_value:
            jsValues = json.loads(kwargs['instance'].custom_field_value)
            js_keys, js_values = [], []
            for idx in range(len(jsValues)):
                for k, v in jsValues[idx].items():
                    js_keys.append(k)
                    js_values.append(v)
            self.fields['jsonColumnName'].initial = js_keys
            self.fields['jsonColumnValue'].initial = js_values
            self.fields['remove_row'].initial = list(range(max(len(jsValues), 1)))




