/*
 * Copyright (c) 2015 Diona Ltd.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of Diona
 * ("Confidential Information"). You shall not disclose such Confidential Information
 * and shall use it only in accordance with the terms of the license agreement you
 * entered into with Diona.
 */
package org.odk.collect.android.widgets;

import android.content.Context;
import android.graphics.Paint;
import android.support.v4.content.ContextCompat;
import android.text.TextPaint;
import android.text.TextUtils;
import android.view.LayoutInflater;

import com.github.mikephil.charting.charts.BarChart;
import com.github.mikephil.charting.components.AxisBase;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarDataSet;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.formatter.IAxisValueFormatter;
import com.github.mikephil.charting.formatter.IValueFormatter;
import com.github.mikephil.charting.utils.ViewPortHandler;

import org.javarosa.core.model.data.IAnswerData;
import org.javarosa.form.api.FormEntryPrompt;
import org.odk.collect.android.R;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * A QuestionWidget that displays data in a BarChart.
 */
public class BarChartWidget extends QuestionWidget {

  private static final int ANIMATION_DURATION_MILLIS = 1000;
  private static final float BAR_WIDTH = 0.9f;
  private static final int BAR_BORDER_WIDTH = 1;
  private final int aboveSolidColor;
  private final int belowSolidColor;
  private final int aboveOutlineColor;
  private final int belowOutlineColor;

  private IAxisValueFormatter yAxisValueFormatter = new IAxisValueFormatter() {
    private final DecimalFormat negativeFormatter = new DecimalFormat("#");
    // We add an extra space into the positive formatter to make up for not having a minus symbol.
    private final DecimalFormat positiveFormatter = new DecimalFormat(" #");

    @Override
    public String getFormattedValue(final float value, final AxisBase axis) {
      final DecimalFormat formatter = value > 0 ? positiveFormatter : negativeFormatter;
      return formatter.format(value);
    }

    @Override
    public int getDecimalDigits() {
      return 1;
    }
  };

  /**
   * @param context
   *     the Context in which the View will be inflated.
   * @param p
   */
  public BarChartWidget(final Context context, final FormEntryPrompt p) {
    super(context, p);

    aboveSolidColor = ContextCompat.getColor(context, R.color.bar_chart_above_solid);
    belowSolidColor = ContextCompat.getColor(context, R.color.bar_chart_below_solid);
    aboveOutlineColor = ContextCompat.getColor(context, R.color.bar_chart_above_outline);
    belowOutlineColor = ContextCompat.getColor(context, R.color.bar_chart_below_outline);

    final String dataText = p.getLongText();
    final String[] dataSets = dataText.split(";");
    final List<Data> positiveData = new ArrayList<>();
    final List<Data> negativeData = new ArrayList<>();
    final String colon = ":";
    final int length = dataSets.length;
    for (int i = 0; i < length; i++) {
      final String dataSet = dataSets[i];
      final int colonPosition = dataSet.indexOf(colon);
      final String label = dataSet.substring(0, colonPosition);
      final int value = Integer.parseInt(dataSet.substring(colonPosition + 1));
      if (value > 0) {
        positiveData.add(new Data(i, value, label));
        negativeData.add(new Data(i, 0, label));
      } else {
        positiveData.add(new Data(i, 0, label));
        negativeData.add(new Data(i, value, label));
      }
    }

    final BarChart topBarChart = getBarChart(context);
    final BarChart bottomBarChart = getBarChart(context);
    setData(topBarChart, positiveData, true);
    setData(bottomBarChart, negativeData, false);
    addView(topBarChart);
    addView(bottomBarChart);
    topBarChart.animateY(ANIMATION_DURATION_MILLIS);
    bottomBarChart.animateY(ANIMATION_DURATION_MILLIS);
  }

  private BarChart getBarChart(final Context context) {
    final BarChart barChart = (BarChart) LayoutInflater.from(context).inflate(R.layout.bar_chart, this, false);
    barChart.setDrawGridBackground(false);
    barChart.setPinchZoom(false);
    barChart.setFitBars(true);
    barChart.setDescription("");
    return barChart;
  }

  private void setData(final BarChart barChart, final List<Data> dataList, final boolean includeLabels) {

    ArrayList<BarEntry> values = new ArrayList<>();

    final int solidColor = includeLabels ? aboveSolidColor : belowSolidColor;
    final int outlineColor = includeLabels ? aboveOutlineColor : belowOutlineColor;

    final int size = dataList.size();
    for (int i = 0; i < size; i++) {
      Data d = dataList.get(i);
      BarEntry entry = new BarEntry(d.xValue, d.yValue);
      values.add(entry);
    }

    BarDataSet set;

    if (barChart.getData() != null && barChart.getData().getDataSetCount() > 0) {
      set = (BarDataSet) barChart.getData().getDataSetByIndex(0);
      set.setValues(values);
      barChart.getData().notifyDataChanged();
      barChart.notifyDataSetChanged();
    } else {
      set = new BarDataSet(values, "Values");
      set.setColor(solidColor);
      set.setBarBorderColor(outlineColor);
      set.setBarBorderWidth(BAR_BORDER_WIDTH);
      set.setValueFormatter(new IValueFormatter() {
        @Override
        public String getFormattedValue(final float value, final Entry entry, final int dataSetIndex, final
        ViewPortHandler viewPortHandler) {
          return String.valueOf((int) value);
        }
      });

      BarData data = new BarData(set);
      data.setBarWidth(BAR_WIDTH);
      data.setHighlightEnabled(false);

      XAxis xAxis = barChart.getXAxis();
      if (includeLabels) {
        xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
      } else {
        xAxis.setPosition(XAxis.XAxisPosition.TOP);
      }
      xAxis.setDrawGridLines(false);
      xAxis.setLabelCount(size);
      xAxis.setDrawLabels(includeLabels);
      xAxis.setDrawAxisLine(true);
      xAxis.setValueFormatter(new XAxisFormatter(dataList));
      xAxis.setTextSize(14);

      YAxis leftAxis = barChart.getAxisLeft();
      leftAxis.setValueFormatter(yAxisValueFormatter);
      leftAxis.setDrawGridLines(false);
      leftAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART);
      leftAxis.setDrawAxisLine(false);
      leftAxis.setDrawLabels(false);

      YAxis rightAxis = barChart.getAxisRight();
      rightAxis.setDrawGridLines(false);
      rightAxis.setValueFormatter(yAxisValueFormatter);
      rightAxis.setDrawAxisLine(false);
      rightAxis.setDrawLabels(false);
      if (includeLabels) {
        //xAxis.setYOffset(0);
        barChart.setExtraBottomOffset(10);
        rightAxis.setAxisMinimum(0);
        leftAxis.setAxisMinimum(0);
      } else {
        barChart.setExtraTopOffset(0);
        rightAxis.setSpaceTop(0);
        rightAxis.setAxisMaximum(0);
        leftAxis.setSpaceTop(0);
        leftAxis.setAxisMaximum(0);

      }

      barChart.getLegend().setTextSize(6f);
      barChart.getLegend().setFormSize(2f);
      barChart.getLegend().setEnabled(false);
      barChart.setData(data);
      barChart.invalidate();
      barChart.setPinchZoom(false);
      barChart.setDoubleTapToZoomEnabled(false);
    }
  }

  /**
   * Demo class representing data.
   */
  private class Data {

    private String xAxisValue;
    private int yValue;
    private int xValue;

    public Data(int xValue, int yValue, String xAxisValue) {
      this.xAxisValue = xAxisValue;
      this.yValue = yValue;
      this.xValue = xValue;
    }
  }

  private class XAxisFormatter implements IAxisValueFormatter {

    private TextPaint mTextPaint;
    private final List<Data> dataList;
    private float avail;

    private XAxisFormatter(final List<Data> dataList) {
      this.dataList = dataList;
      mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
      avail = getContext().getResources().getDimension(R.dimen.barchart_label_width);
    }

    @Override
    public String getFormattedValue(final float value, final AxisBase axis) {
      final String xAxisValue = dataList.get((int) value).xAxisValue;
      return TextUtils.ellipsize(xAxisValue, mTextPaint, avail, TextUtils.TruncateAt.MIDDLE).toString();
    }

    @Override
    public int getDecimalDigits() {
      return 0;
    }
  }

  @Override
  boolean showHelp() {
    return false;
  }

  @Override
  boolean showQuestion() {
    return false;
  }

  @Override
  public IAnswerData getAnswer() {
    return null;
  }

  @Override
  public void clearAnswer() {

  }

  @Override
  public void setFocus(final Context context) {

  }

  @Override
  public void setOnLongClickListener(final OnLongClickListener l) {

  }
}
