#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Yahoo! Finance market data downloader (+fix for Pandas Datareader)
# https://github.com/ranaroussi/yfinance
#
# Copyright 2017-2019 Ran Aroussi
#
# Licensed 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 __future__ import print_function

import requests as _requests
import re as _re
import pandas as _pd
import numpy as _np
import sys as _sys
import re as _re

import json as _json


def empty_df(index=[]):
    empty = _pd.DataFrame(
        index=index,
        data={
            "Open": _np.nan,
            "High": _np.nan,
            "Low": _np.nan,
            "Close": _np.nan,
            "AdjClose": _np.nan,
            "Volume": _np.nan,
        },
    )
    empty.index.name = "Date"
    return empty


def get_json(url, proxy=None):
    html = _requests.get(url=url, proxies=proxy).text

    if "QuoteSummaryStore" not in html:
        html = _requests.get(url=url, proxies=proxy).text
        if "QuoteSummaryStore" not in html:
            return {}

    json_str = html.split("root.App.main =")[1].split("(this)")[0].split(";\n}")[0].strip()

    # 2/29/20 there is a bug where the
    # return value is 7.495B for MSFT other current assets
    # but the display value on the web is 7.473B
    # this is from the api -- don't know how ui gets it right....

    # # vsc will only allow 64K of text in watch copy :(
    #     with open("temp.json", "w") as file:
    #         file.write(json_str)

    data = _json.loads(json_str)["context"]["dispatcher"]["stores"]["QuoteSummaryStore"]

    # return data
    new_data = _json.dumps(data).replace("{}", "null")
    new_data = _re.sub(r"\{[\'|\"]raw[\'|\"]:(.*?),(.*?)\}", r"\1", new_data)

    return _json.loads(new_data)


def camel2title(o):
    return [_re.sub("([a-z])([A-Z])", "\g<1> \g<2>", i).title() for i in o]


def auto_adjust(data):
    df = data.copy()
    ratio = df["Close"] / df["AdjClose"]
    df["Adj Open"] = df["Open"] / ratio
    df["Adj High"] = df["High"] / ratio
    df["Adj Low"] = df["Low"] / ratio

    df.drop(["Open", "High", "Low", "Close"], axis=1, inplace=True)

    df.rename(
        columns={"Adj Open": "Open", "Adj High": "High", "Adj Low": "Low", "AdjClose": "Close",}, inplace=True,
    )

    df = df[["Open", "High", "Low", "Close", "Volume"]]
    return df[["Open", "High", "Low", "Close", "Volume"]]


def back_adjust(data):
    """ back-adjusted data to mimic true historical prices """

    df = data.copy()
    ratio = df["AdjClose"] / df["Close"]
    df["Adj Open"] = df["Open"] * ratio
    df["Adj High"] = df["High"] * ratio
    df["Adj Low"] = df["Low"] * ratio

    df.drop(["Open", "High", "Low", "AdjClose"], axis=1, inplace=True)

    df.rename(
        columns={"Adj Open": "Open", "Adj High": "High", "Adj Low": "Low"}, inplace=True,
    )

    return df[["Open", "High", "Low", "Close", "Volume"]]


def parse_quotes(data, tz=None):
    timestamps = data["timestamp"]
    ohlc = data["indicators"]["quote"][0]
    volumes = ohlc["volume"]
    opens = ohlc["open"]
    closes = ohlc["close"]
    lows = ohlc["low"]
    highs = ohlc["high"]

    adjclose = closes
    if "adjclose" in data["indicators"]:
        adjclose = data["indicators"]["adjclose"][0]["adjclose"]

    quotes = _pd.DataFrame(
        {"Open": opens, "High": highs, "Low": lows, "Close": closes, "AdjClose": adjclose, "Volume": volumes,}
    )

    quotes.index = _pd.to_datetime(timestamps, unit="s")
    quotes.sort_index(inplace=True)

    if tz is not None:
        quotes.index = quotes.index.tz_localize(tz)

    return quotes


def parse_actions(data, tz=None):
    dividends = _pd.DataFrame(columns=["Dividends"])
    splits = _pd.DataFrame(columns=["StockSplits"])

    if "events" in data:
        if "dividends" in data["events"]:
            dividends = _pd.DataFrame(data=list(data["events"]["dividends"].values()))
            dividends.set_index("date", inplace=True)
            dividends.index = _pd.to_datetime(dividends.index, unit="s")
            dividends.sort_index(inplace=True)
            if tz is not None:
                dividends.index = dividends.index.tz_localize(tz)

            dividends.columns = ["Dividends"]

        if "splits" in data["events"]:
            splits = _pd.DataFrame(data=list(data["events"]["splits"].values()))
            splits.set_index("date", inplace=True)
            splits.index = _pd.to_datetime(splits.index, unit="s")
            splits.sort_index(inplace=True)
            if tz is not None:
                splits.index = splits.index.tz_localize(tz)
            splits["StockSplits"] = splits["numerator"] / splits["denominator"]
            splits = splits["StockSplits"]

    return dividends, splits


class ProgressBar:
    def __init__(self, iterations, text="completed"):
        self.text = text
        self.iterations = iterations
        self.prog_bar = "[]"
        self.fill_char = "*"
        self.width = 50
        self.__update_amount(0)
        self.elapsed = 1

    def completed(self):
        if self.elapsed > self.iterations:
            self.elapsed = self.iterations
        self.update_iteration(1)
        print("\r" + str(self), end="")
        _sys.stdout.flush()
        print()

    def animate(self, iteration=None):
        if iteration is None:
            self.elapsed += 1
            iteration = self.elapsed
        else:
            self.elapsed += iteration

        print("\r" + str(self), end="")
        _sys.stdout.flush()
        self.update_iteration()

    def update_iteration(self, val=None):
        val = val if val is not None else self.elapsed / float(self.iterations)
        self.__update_amount(val * 100.0)
        self.prog_bar += "  %s of %s %s" % (self.elapsed, self.iterations, self.text,)

    def __update_amount(self, new_amount):
        percent_done = int(round((new_amount / 100.0) * 100.0))
        all_full = self.width - 2
        num_hashes = int(round((percent_done / 100.0) * all_full))
        self.prog_bar = "[" + self.fill_char * num_hashes + " " * (all_full - num_hashes) + "]"
        pct_place = (len(self.prog_bar) // 2) - len(str(percent_done))
        pct_string = "%d%%" % percent_done
        self.prog_bar = self.prog_bar[0:pct_place] + (pct_string + self.prog_bar[pct_place + len(pct_string) :])

    def __str__(self):
        return str(self.prog_bar)
