<script setup>
import {
  ref,
  onMounted,
  onUnmounted,
  watch,
  defineExpose,
  defineProps,
} from 'vue';
import {
  createChart,
  LineSeries,
  AreaSeries,
  BarSeries,
  CandlestickSeries,
  HistogramSeries,
  BaselineSeries,
} from 'lightweight-charts';

const props = defineProps({
  type: {
    type: String,
    default: 'line',
  },
  data: {
    type: Array,
    required: true,
  },
  ema: {
    type: Array,
    required: true,
  },
  macd: {
    type: Object,
  },
  bb: {
    type: Object,
  },
  rsi: {
    type: Object,
  },
  autosize: {
    default: true,
    type: Boolean,
  },
  chartOptions: {
    type: Object,
  },
  emaOptions: {
    type: Object,
  },
  candleOptions: {
    type: Object,
  },
  macdLineOptions: {
    type: Object,
  },
  macdSignalOptions: {
    type: Object,
  },
  macdHistogramOptions: {
    type: Object,
  },
  rsiLineOptions: {
    type: Object,
  },
  upperBbOptions: {
    type: Object,
  },
  middleBbOptions: {
    type: Object,
  },
  lowerBbOptions: {
    type: Object,
  },
  timeScaleOptions: {
    type: Object,
  },
  priceScaleOptions: {
    type: Object,
  },
  lazyLock: {
    type: Boolean,
    default: false,
  },
});

const emits = defineEmits(['lazyLoad', 'mouseMove'])

function getChartSeriesDefinition(type) {
  switch (type.toLowerCase()) {
    case 'line':
      return LineSeries;
    case 'area':
      return AreaSeries;
    case 'bar':
      return BarSeries;
    case 'candlestick':
      return CandlestickSeries;
    case 'histogram':
      return HistogramSeries;
    case 'baseline':
      return BaselineSeries;
  }
  return LineSeries;
}

// Lightweight Charts™ instances are stored as normal JS variables
// If you need to use a ref then it is recommended that you use `shallowRef` instead
let emaLine;
let candlestick;
let chart;

let macdLine;
let macdSignalLine;
let macdHistogram;
let macdPane;
let rsiPane;

let upperBb;
let middleBb;
let lowerBb;

let rsiLine;

const chartContainer = ref();

const fitContent = () => {
  if (!chart) return;
  chart.timeScale().fitContent();
};

const getChart = () => {
  return chart;
};

defineExpose({fitContent, getChart});

// Auto resizes the chart when the browser window is resized.
const resizeHandler = () => {
  if (!chart || !chartContainer.value) return;
  const dimensions = chartContainer.value.getBoundingClientRect();
  chart.resize(dimensions.width, dimensions.height);
};

// Creates the chart series and sets the data.
const addSeriesAndData = props => {
  const candleStickDefinition = getChartSeriesDefinition(props.type);
  candlestick = chart.addSeries(candleStickDefinition, props.candleOptions);
  let oldT = 0;
  let list = []
  props.data.forEach(item => {
    if (item.t !== oldT) {
      list.push({
        time: Number(item.t),
        open: Number(item.o),
        high: Number(item.h),
        low: Number(item.l),
        close: Number(item.c),
        volume: Number(item.v),
      })
    }
    oldT = item.t
  })
  candlestick.setData(list);

  const seriesDefinition = getChartSeriesDefinition('line')
  emaLine = chart.addSeries(seriesDefinition, props.emaOptions)
  emaLine.setData(props.ema)

  macdLine = macdPane.addSeries(seriesDefinition, props.macdLineOptions)
  macdSignalLine = macdPane.addSeries(seriesDefinition, props.macdSignalOptions)
  macdHistogram = macdPane.addSeries(getChartSeriesDefinition('histogram'), props.macdHistogramOptions)
  if(Object.keys(props.macd).length) {
    macdLine.setData(props.macd.m)
    macdHistogram.setData(props.macd.h)
    macdSignalLine.setData(props.macd.s)
  }

  upperBb = chart.addSeries(seriesDefinition, props.upperBbOptions);
  middleBb = chart.addSeries(seriesDefinition, props.middleBbOptions);
  lowerBb = chart.addSeries(seriesDefinition, props.lowerBbOptions);
  if(Object.keys(props.macd).length) {
    upperBb.setData(props.bb.u);
    middleBb.setData(props.bb.m);
    lowerBb.setData(props.bb.l);
  }

  rsiLine = rsiPane.addSeries(seriesDefinition, props.rsiLineOptions)
  if(Object.keys(props.macd).length) {
    rsiLine.setData(props.rsi)
  }
  // console.log(3)
};

onMounted(() => {
  // Create the Lightweight Charts Instance using the container ref.
  chart = createChart(chartContainer.value, props.chartOptions);
  macdPane = chart.addPane();
  macdPane.setStretchFactor(0.5)
  rsiPane = chart.addPane();
  rsiPane.setStretchFactor(0.4)

  addSeriesAndData(props);

  if (props.priceScaleOptions) {
    chart.priceScale().applyOptions(props.priceScaleOptions);
  }

  if (props.timeScaleOptions) {
    chart.timeScale().applyOptions(props.timeScaleOptions);
  }

  chart.timeScale().fitContent();

  if (props.autosize) {
    window.addEventListener('resize', resizeHandler);
  }
  chart.timeScale().subscribeVisibleLogicalRangeChange(onVisibleLogicalRangeChanged);

  chart.subscribeCrosshairMove(onCrosshairMove)

  chart.subscribeClick(onClick);

});

function onClick(dd) {
  console.log(dd)
}

function onVisibleLogicalRangeChanged(newVisibleLogicalRange) {
  const barsInfo = candlestick.barsInLogicalRange(newVisibleLogicalRange);
  // if there less than 50 bars to the left of the visible area
  if (barsInfo !== null && barsInfo.barsBefore < 500 && !props.lazyLock) {

    // try to load additional historical data and prepend it to the series data
    emits('lazyLoad', props.data[0].t - 5000);
  }
}

function onCrosshairMove(param) {

  if (param.point === null || param.time === undefined) {
    return;
  }
  emits('mouseMove', param.seriesData.get(candlestick))
  //
  // // 현재 포인터 아래의 캔들 데이터 가져오기
  // const data = param.seriesData.get(candleSeries);
  // console.log(data)
}

onUnmounted(() => {
  if (chart) {
    chart.remove();
    chart = null;
  }
  if (candlestick) {
    candlestick = null;
  }
  window.removeEventListener('resize', resizeHandler);
});

/*
 * Watch for changes to any of the component properties.

 * If an options property is changed then we will apply those options
 * on top of any existing options previously set (since we are using the
 * `applyOptions` method).
 *
 * If there is a change to the chart type, then the existing series is removed
 * and the new series is created, and assigned the data.
 *
 */
watch(
    () => props.autosize,
    enabled => {
      if (!enabled) {
        window.removeEventListener('resize', resizeHandler);
        return;
      }
      window.addEventListener('resize', resizeHandler);
    }
);

watch(
    () => props.type,
    newType => {
      if (candlestick && chart) {
        chart.removeSeries(candlestick);
      }
      addSeriesAndData(props);
    }
);

watch(
    () => props.data,
    newData => {
      if (!candlestick) return;
      let oldT = 0;
      let list = []
      newData.forEach(item => {
        if (item.t !== oldT) {

          list.push({
            time: item.t,
            open: Number(item.o),
            high: Number(item.h),
            low: Number(item.l),
            close: Number(item.c),
            volume: Number(item.v),
          })
        }
        oldT = item.t
      })
      candlestick.setData(list);
    }
);

watch(
    () => props.ema,
    newData => {
      if (!emaLine) return;
      emaLine.setData(newData);
    }
);

watch(
    () => props.macd,
    newData => {
      if (!macdLine || !macdHistogram || !macdSignalLine) return;
      macdLine.setData(newData.m);
      macdHistogram.setData(newData.h);
      macdSignalLine.setData(newData.s);
    }
);


watch(
    () => props.bb,
    newData => {
      if (!upperBb || !middleBb || !lowerBb) return;
      upperBb.setData(newData.u);
      middleBb.setData(newData.m);
      lowerBb.setData(newData.l);
    }
);


watch(
    () => props.rsi,
    newData => {
      if (!rsiLine) return;
      rsiLine.setData(props.rsi);
    }
);

watch(
    () => props.chartOptions,
    newOptions => {
      if (!chart) return;
      chart.applyOptions(newOptions);
    }
);

watch(
    () => props.candleOptions,
    newOptions => {
      if (!candlestick) return;
      candlestick.applyOptions(newOptions);
    }
);

watch(
    () => props.emaOptions,
    newOptions => {
      if (!emaLine) return;
      emaLine.applyOptions(newOptions);
    }
);

watch(
    () => props.macdLineOptions,
    newOptions => {
      if (!macdLine) return;
      macdLine.applyOptions(newOptions);
    }
);

watch(
    () => props.macdSignalOptions,
    newOptions => {
      if (!macdSignalLine) return;
      macdSignalLine.applyOptions(newOptions);
    }
);

watch(
    () => props.macdHistogramOptions,
    newOptions => {
      if (!macdHistogram) return;
      macdHistogram.applyOptions(newOptions);
    }
);

watch(
    () => props.priceScaleOptions,
    newOptions => {
      if (!chart) return;
      chart.priceScale().applyOptions(newOptions);
    }
);

watch(
    () => props.timeScaleOptions,
    newOptions => {
      if (!chart) return;
      chart.timeScale().applyOptions(newOptions);
    }
);
</script>

<template>
  <div class="lw-chart" ref="chartContainer"></div>
</template>

<style scoped>
.lw-chart {
  height: 100%;
}
</style>
