

import 'dart:convert';

import 'package:flutter/material.dart';

import '../../../events/eventbus.dart';
import '../../../events/events.dart';
import '../../../generated/l10n.dart';
import '../../../models/product.dart';
import '../../../models/product_option.dart';
import '../../../utils/utils.dart';
import 'options_base.dart';
import 'rules.dart';

class QtyOptions extends OptionsBase {
  QtyOptions({@required Product product, @required int index, @required Map<String, dynamic> selections})
    : super(product: product, index: index, selections: selections);

  @override
  State<StatefulWidget> createState() {
    return new QtyOptionsState();
  }
}

class QtyOptionsState extends OptionsBaseState<QtyOptions> {
  Map<String, dynamic> optionsState = new Map<String, dynamic>();
  int thisLimitQty = 0;

  @override
  Widget build(BuildContext context) {

    return new ListView.builder(
      itemCount: 2,
      itemBuilder: (context, index) {
        if (index == 0) {
          return new Container(
            padding: new EdgeInsets.all(10.0),
            child: new Column(
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                new Text(
                  S.of(context).check_option_select_token(product.productAttributes[this.index].name),
                  style: new TextStyle(
                    fontSize: 12.5,
                  ),
                  overflow: TextOverflow.ellipsis,
                ),
                new Text(
                  product.productAttributes[this.index].required ? S.of(context).check_option_is_required : S.of(context).check_option_is_optional,
                  style: new TextStyle(
                    fontSize: 10.0,
                    color: new Color(0xFF999999)
                  ),
                  overflow: TextOverflow.ellipsis,
                )
              ],
            ),
          );
        }
        return _getOptionWidget();
      }
    );
  }

  @override
  void initState() {
    super.initState();
    setState(() {
      product = widget.product;
      selections = widget.selections;
      index = widget.index;
    });
    eventBus.on<OnAttributePageChanged>().listen((event) {
      setState(() {
        index = event.index;
      });
    });
  }

  void _onOptionTappedCallback(name, quantity, adjustAmount, int optIndex, ProductOption productOption) {
    Map<String, dynamic> opt = {
      'name': name,
      'quantity': quantity,
      'adjust_amount': adjustAmount
    };
    var cloneSelections = json.decode(json.encode(selections));
    int idx = Utils.selectionsContains(cloneSelections, product.productAttributes[index].name, name);
    if (idx != -1) {
      if (quantity == 1) {
        cloneSelections[product.productAttributes[index].name.toUpperCase()][idx]['quantity'] += 1;
        optionsState[product.productAttributes[index].name][optIndex]['quantity'] = cloneSelections[product.productAttributes[index].name.toUpperCase()][idx]['quantity'];
      } else {
        if (cloneSelections[product.productAttributes[index].name.toUpperCase()][idx]['quantity'] - 1 > 0) {
          cloneSelections[product.productAttributes[index].name.toUpperCase()][idx]['quantity'] -= 1;
          optionsState[product.productAttributes[index].name][optIndex]['quantity'] = cloneSelections[product.productAttributes[index].name.toUpperCase()][idx]['quantity'];
        } else {
          (cloneSelections[product.productAttributes[index].name.toUpperCase()] as List).removeAt(idx);
          optionsState[product.productAttributes[index].name][optIndex]['quantity'] = 0;
        }
      }
    } else if (cloneSelections.containsKey(product.productAttributes[index].name.toUpperCase())) {
      (cloneSelections[product.productAttributes[index].name.toUpperCase()] as List).add(opt);
      optionsState[product.productAttributes[index].name][optIndex]['quantity'] = 1;
    } else {
      cloneSelections[product.productAttributes[index].name.toUpperCase()] = [opt];
      optionsState[product.productAttributes[index].name][optIndex]['quantity'] = 1;
    }

    if (idx != -1 && (cloneSelections[product.productAttributes[index].name.toUpperCase()] as List).length == 0) {
      cloneSelections.remove(product.productAttributes[index].name.toUpperCase());
    }

    setOptionsStateDisabled(product.productAttributes[index].name, false);

    setState(() {
      selections = cloneSelections;
    });
    eventBus.fire(new OnAttributeSelectionsChanged(selections));
  }

  Widget _getOptionWidget() {
    var row = Wrap(
      children: <Widget>[],
    );

    List<ProductOption> productOptions = product.productAttributes[index].productOptions;

    if (!optionsState.containsKey(product.productAttributes[index].name)) {
      List<Map<String, dynamic>> optionState = [];
      for (var i = 0; i < productOptions.length; i++) {
        int qty = 0;
        int idx = Utils.selectionsContains(selections, product.productAttributes[index].name, productOptions[i].name);
        if (idx != -1) {
          qty = selections[product.productAttributes[index].name.toUpperCase()][idx]['quantity'];
        }
        optionState.add({'name': product.productAttributes[index].productOptions[i].name, 'disabled': false, 'quantity': qty, 'check': false});
      }
      optionsState[product.productAttributes[index].name] = optionState;
    }

    if (selections.containsKey(product.productAttributes[index].name.toUpperCase())) {
      Map<String, dynamic> attrExtraJson = Utils.stringToJson(
          product.productAttributes[index].extra);
      if (attrExtraJson != null) {
        var selectLimitIfFieldEqualsTo = Rule.getRule(
            attrExtraJson, Rule.RULE_SELECT_LIMIT_IF_FIELD_EQUALS_TO);
        if (selectLimitIfFieldEqualsTo != null) {
          if (selectLimitIfFieldEqualsTo is List) {
            for (var b = 0; b < (selectLimitIfFieldEqualsTo as List).length; b++) {
              Map<String, dynamic> selectLimitIfFieldEqualsTo1 = selectLimitIfFieldEqualsTo[b];
              if (selectLimitIfFieldEqualsTo1.containsKey(
                  Rule.RULE_KEY_FORCE_LIMITED)) {
                int limitQty = selectLimitIfFieldEqualsTo1[Rule
                    .RULE_KEY_FORCE_LIMITED];
                thisLimitQty = limitQty;
                if ((selections[product.productAttributes[index].name
                    .toUpperCase()] as List).length >= limitQty) {
                  disableOptionIfNotSelected();
                }
              } else if (Utils.getSelectedAttributeValue(selections, selectLimitIfFieldEqualsTo1[Rule.RULE_KEY_FIELD_KEY]).length > 0) {
                if (selectLimitIfFieldEqualsTo1.containsKey(Utils.getSelectedAttributeValue(selections, selectLimitIfFieldEqualsTo1[Rule.RULE_KEY_FIELD_KEY])[0])) {
                  int limitQty = selectLimitIfFieldEqualsTo1[Utils.getSelectedAttributeValue(selections, selectLimitIfFieldEqualsTo1[Rule.RULE_KEY_FIELD_KEY])[0]];
                  thisLimitQty = limitQty;
                  if ((selections[product.productAttributes[index].name
                      .toUpperCase()] as List).length >= limitQty) {
                    disableOptionIfNotSelected();
                  }
                }
              }
            }
          } else {
            if (selectLimitIfFieldEqualsTo.containsKey(
                Rule.RULE_KEY_FORCE_LIMITED)) {
              int limitQty = selectLimitIfFieldEqualsTo[Rule
                  .RULE_KEY_FORCE_LIMITED];
              thisLimitQty = limitQty;
              if ((selections[product.productAttributes[index].name
                  .toUpperCase()] as List).length >= limitQty) {
                disableOptionIfNotSelected();
              }
            } else if (Utils.getSelectedAttributeValue(selections, selectLimitIfFieldEqualsTo[Rule.RULE_KEY_FIELD_KEY]).length > 0) {
              if (selectLimitIfFieldEqualsTo.containsKey(Utils.getSelectedAttributeValue(selections, selectLimitIfFieldEqualsTo[Rule.RULE_KEY_FIELD_KEY])[0])) {
                int limitQty = selectLimitIfFieldEqualsTo[Utils.getSelectedAttributeValue(selections, selectLimitIfFieldEqualsTo[Rule.RULE_KEY_FIELD_KEY])[0]];
                thisLimitQty = limitQty;
                if ((selections[product.productAttributes[index].name
                    .toUpperCase()] as List).length >= limitQty) {
                  disableOptionIfNotSelected();
                }
              }
            }
          }
        }
      }

      for (var i = 0; i < productOptions.length; i++) {
        Map<String, dynamic> extraJson = Utils.stringToJson(
            productOptions[i].extra);
        if (extraJson != null) {
          Map<String, dynamic> exclusiveRule = Rule.getRule(
              extraJson, Rule.RULE_EXCLUSIVE_SELECTION);
          Map<String, dynamic> multiItemRule = Rule.getRule(extraJson, Rule.RULE_ACTUAL_QTY_IS);
          if (exclusiveRule != null) {
            if (thisLimitQty > 0 && !_checkOptionIsCheck(productOptions[i].name)) {
              optionsState[product.productAttributes[index].name][i]['disabled'] = true;
            } else {
              if (_checkOptionIsCheck(productOptions[i].name)) {
                setOptionsStateDisabled(
                    product.productAttributes[index].name, true);
                optionsState[product.productAttributes[index]
                    .name][i]['disabled'] = false;
                break;
              }
            }
          }
          if (multiItemRule != null) {
            if (_checkOptionIsCheck(productOptions[i].name)) {
              if (multiItemRule[Rule.RULE_ACTUAL_QTY_IS] > thisLimitQty - (selections[product.productAttributes[index].name.toUpperCase()] as List).length) {
                disableOptionIfNotSelected();
              }
            } else {
              if (multiItemRule[Rule.RULE_ACTUAL_QTY_IS] > thisLimitQty - (selections[product.productAttributes[index].name.toUpperCase()] as List).length) {
                optionsState[product.productAttributes[index]
                    .name][i]['disabled'] = true;
              }
            }
          }
        }
      }
    }

    List<Map<String, dynamic>> optionState = optionsState[product.productAttributes[index].name];
    for (var i = 0; i < optionState.length; i++) {
      Widget optionWidget = _getOptionQty(
          product.productAttributes[index].productOptions[i], optionState[i]['disabled'], optionState[i]['quantity'], i);
      row.children.add(optionWidget);
    }
    return row;
  }

  void disableOptionIfNotSelected() {
    setOptionsStateDisabled(product.productAttributes[index].name, true);
    for (var i = 0; i < optionsState[product.productAttributes[index].name].length; i++) {
      if (Utils.selectionsContains(selections, product.productAttributes[index].name, optionsState[product.productAttributes[index].name][i]['name']) != -1) {
        optionsState[product.productAttributes[index].name][i]['disabled'] = false;
      }
    }
  }

  bool _checkOptionIsCheck(String name) {
    if (Utils.selectionsContains(selections, product.productAttributes[index].name, name) != -1) {
      return true;
    }
    return false;
  }

  Widget _getOptionQty(ProductOption productOption, bool optDisabled, int quantity, int optIndex) {
    bool disabled = optDisabled;
    bool check = _checkOptionIsCheck(productOption.name);

    Map<String, dynamic> extraJson = Utils.stringToJson(productOption.extra);
//    Utils.jsonPrettyPrint(extraJson);

    double extraAdjustAmount = 0.0;

    if (extraJson != null) {
      var sizeBaseAdjustment = Rule.getRule(extraJson, Rule.RULE_ADJUSTMENT_BASED_ON_SELECTED_FIELD_VALUE);
      if (sizeBaseAdjustment != null) {
        if (sizeBaseAdjustment is List) {
          for (var i = 0; i < (sizeBaseAdjustment as List).length; i++) {
            Map<String, dynamic> sizeBaseAdjustment1 = sizeBaseAdjustment[i];
            List<String> attValues = Utils.getSelectedAttributeValue(selections, sizeBaseAdjustment1[Rule.RULE_KEY_FIELD_KEY]);
            if (attValues.length > 0 && sizeBaseAdjustment1.containsKey(attValues[0])) {
              extraAdjustAmount += sizeBaseAdjustment1[attValues[0]];
            }
          }
        } else {
          List<String> attValues = Utils.getSelectedAttributeValue(selections, sizeBaseAdjustment[Rule.RULE_KEY_FIELD_KEY]);
          if (attValues.length > 0 && sizeBaseAdjustment.containsKey(attValues[0])) {
            extraAdjustAmount += sizeBaseAdjustment[attValues[0]];
          }
        }
      }

      var disabledRule =
      Rule.getRule(extraJson, Rule.RULE_DISABLED_IF_FIELD_EQUALS_TO);
      if (disabledRule != null) {
        if (disabledRule is List) {
          for (var i = 0; i < (disabledRule as List).length; i++) {
            Map<String, dynamic> disabledRule1 = disabledRule[i];
            List<String> attValues = Utils.getSelectedAttributeValue(
                selections, disabledRule1[Rule.RULE_KEY_FIELD_KEY]);
            if (attValues.length > 0 &&
                disabledRule1.containsKey(attValues[0])) {
              disabled = true;
            }
            if (attValues.length > 0) {
              for (String s in attValues) {
                if (disabledRule1.containsKey(s) && disabledRule1[s]) {
                  disabled = true;
                  break;
                }
              }
            }
            if (disabled) {
              break;
            }
          }
        } else {
          List<String> attValues = Utils.getSelectedAttributeValue(
              selections, disabledRule[Rule.RULE_KEY_FIELD_KEY]);
          if (attValues.length > 0) {
            for (String s in attValues) {
              if (disabledRule.containsKey(s) && disabledRule[s]) {
                disabled = true;
                break;
              }
            }
          }
        }
      }
    }

    return new Container(
      width: 100.0,
      height: 100.0,
      decoration: BoxDecoration(
        color: disabled ? disabledBackgroundColor : (check ? selectedBackgroundColor : deselectBackgroundColor),
        shape: BoxShape.rectangle,
        border:  new Border.all(
          color: check ? selectedBorderColor : deselectBorderColor,
          width: 1.0,
        ),
//          borderRadius: BorderRadius.all(Radius.circular(10.0)),
        borderRadius: BorderRadius.only(
          topLeft: Radius.circular(10.0),
          topRight: Radius.circular(10.0),
        ),
      ),

      margin: new EdgeInsets.all(10.0).copyWith(right: 0.0),
      child: new Column(
        children: <Widget>[
          new GestureDetector(
            child: new Container(
              width: 100.0,
              height: 70.0,
              padding: new EdgeInsets.all(4.0),
              child: new Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  new Text(
                    productOption.name,
                    style: new TextStyle(
                      fontSize: 14.0,
                      color: check ? selectedTextColor : deselectTextColor,
                    ),
                    maxLines: 2,
                    softWrap: true,
                    overflow: TextOverflow.ellipsis,
                  ),
                  new Text(
                    (productOption.adjustAmount + extraAdjustAmount) > 0 ? '+${(productOption.adjustAmount + extraAdjustAmount).toStringAsFixed(2)}' : '',
                    style: new TextStyle(
                      fontSize: 11.0,
                      color: check ? selectedTextColor : new Color(0xFFABABAB),
                    ),
                    overflow: TextOverflow.ellipsis,
                  )
                ],
              ),
            ),
            onTap: () => disabled ? null : _onOptionTappedCallback(
                productOption.name, 1, productOption.adjustAmount + extraAdjustAmount, optIndex, productOption),
          ),
          new Container(
            width: 100.0,
            height: 28.0,
            decoration: BoxDecoration(
              color: disabled ? disabledBackgroundColor : (check ? selectedBackgroundColor : deselectBackgroundColor),
              shape: BoxShape.rectangle,
              border:  new Border(
                top: new BorderSide(
                  color: check ? selectedBorderColor : deselectBorderColor,
                  width: 1.0,
                ),
              ),
            ),
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                new Expanded(
                  child: new GestureDetector(
                    child: new Container(
                      color: new Color(0x00000000),
                      width: 49.0,
                      height: 28.0,
                      child: new Center(
                        child: new Text(
                          '${quantity}',
                          style: new TextStyle(
                            color: check ? selectedTextColor : new Color(0xFFABABAB),
                          ),
                        ),
                      ),
                    ),
                    onTap: () => disabled ? null : _onOptionTappedCallback(
                        productOption.name, 1, productOption.adjustAmount + extraAdjustAmount, optIndex, productOption),
                  ),
                ),
                new Expanded(
                  child: new GestureDetector(
                    child: new Container(
                      color: disabledBackgroundColor,
                      width: 49.0,
                      height: 28.0,
                      child: new Center(
                          child: new Text('-')
                      ),
                    ),
                    onTap: () => (disabled || quantity == 0) ? null : _onOptionTappedCallback(
                        productOption.name, -1, productOption.adjustAmount + extraAdjustAmount, optIndex, productOption),
                  ),
                ),
              ],
            ),
          )
        ],
      ),
    );
  }

  void setOptionsStateDisabled(String attrName, bool disabled) {
    if (optionsState.containsKey(attrName)) {
      List<Map<String, dynamic>> optionState = optionsState[attrName];
      for (var i = 0; i < optionState.length; i++) {
        optionState[i]['disabled'] = disabled;
      }
    }
  }
}