using MarketData.MarketDataModel;
using MarketData.MarketDataModel.GainLoss;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Web;
using Xamarin.Essentials;

namespace MarketData.Service
{
// http://192.168.0.73:8000

  public class MarketDataServiceClient
  {
    private Uri baseUri = null;
    private HttpClient httpClient = null;
    private String accessToken = null;
    private List<Exception> exceptions = new List<Exception>();
    private static MarketDataServiceClient marketDataServiceClient=null;
    private static readonly TimeSpan CONNECTION_TIMEOUT=new TimeSpan(0,0,60);

    private MarketDataServiceClient()
    {
    }
    public static MarketDataServiceClient GetInstance()
    {
      lock(typeof(MarketDataServiceClient))
      {
        if(null==marketDataServiceClient)
        {
          marketDataServiceClient=new MarketDataServiceClient();
        }
        return marketDataServiceClient;
      }
    }
    /// <summary>
    /// SetUrl
    /// </summary>
    /// <param name="url">The fully qualified url http://100.0.0.0:8000.</param>
    /// <param name="user">The user.</param> 
    public void SetUrl(String url,String user)
    {
      lock(this)
      {
        if(String.IsNullOrEmpty(url))return;
        baseUri = new Uri(url);
        httpClient = new HttpClient();
        httpClient.BaseAddress = baseUri;
        httpClient.Timeout=CONNECTION_TIMEOUT;
        ServiceResult serviceResult=Login(user);
        if(!serviceResult.Success)return;
        accessToken=(String)serviceResult.ContextSpecificResult;
      }
    }
    public String GetBaseUri()
    {
      return null==baseUri?"":baseUri.ToString();
    }
    public bool Ping(String url)
    {
      lock(this)
      {
        if(String.IsNullOrEmpty(url))return false;
        HttpClient httpClient=new HttpClient();
        
        try
        {
          httpClient.BaseAddress=new Uri(url);
          httpClient=new HttpClient();
          httpClient.BaseAddress=new Uri(url);
          httpClient.Timeout=new TimeSpan(0,0,10);
          bool result=false;
          if(!IsNetworkAvailable())return false;
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/Ping/GetPing");
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          result = JsonConvert.DeserializeObject<bool>(json);
          return result;
        }
        catch (Exception exception)
        {
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return false;
        }
        finally
        {
          if(null!=httpClient)
          {
            httpClient.Dispose();
            httpClient=null;
          }
        }
      }
    }
    private ServiceResult Login(String user)
    {
      lock(this)
      {
        try
        {
          if(!IsNetworkAvailable())return new ServiceResult(false,"No network.");
          StringBuilder sb = new StringBuilder();
          user = Authorizations.Xor(user);
          sb.Append("/api/Authorization/GetToken?").Append("user=").Append(user);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          String accessToken = JsonConvert.DeserializeObject<String>(json);
          return new ServiceResult(accessToken);
        }
        catch (Exception exception)
        {
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false,exception.ToString());
        }
      }
    }
// ********************************************************** C O N S U M E R  P R I C E  I N D E X  *************************************************
    public ServiceResult GetDistinctConsumerPriceIndices()
    {
      lock (this)
      {
        try
        {
          if (!IsNetworkAvailable()) return new ServiceResult(false, "No network.");
          if (!IsAuthorized()) return new ServiceResult(false, "Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/PriceIndex/GetDistinctPriceIndices?").Append("token=").Append(accessToken);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          List<String> priceIndices = JsonConvert.DeserializeObject<List<String>>(json);
          return new ServiceResult(priceIndices);
        }
        catch (Exception exception)
        {
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false, exception.ToString());
        }
      }
    }

    public ServiceResult GetGetConsumerPriceIndex(String indexCode)
    {
      lock (this)
      {
        try
        {
          if (!IsNetworkAvailable()) return new ServiceResult(false, "No network.");
          if (!IsAuthorized()) return new ServiceResult(false, "Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/PriceIndex/GetConsumerPriceIndex?").Append("token=").Append(accessToken).Append("&").Append("indexCode=").Append(HttpUtility.UrlEncode(indexCode));
          String json = httpClient.GetStringAsync(sb.ToString()).Result;

          List<PriceIndex> priceIndices = JsonConvert.DeserializeObject<List<PriceIndex>>(json);
          return new ServiceResult(priceIndices);
        }
        catch (Exception exception)
        {
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false, exception.ToString());
        }
      }
    }   

// ************************************************************************ P R E M A R K E T  *************************************************
    public ServiceResult GetLatestPremarketData(String market,DateTime marketDate)
    {
      lock (this)
      {
        try
        {
          if (!IsNetworkAvailable()) return new ServiceResult(false, "No network.");
          if (!IsAuthorized()) return new ServiceResult(false, "Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/PreMarket/GetLatestPremarketData?").Append("token=").Append(accessToken).Append("&").Append("market=").Append(HttpUtility.UrlEncode(market));
          sb.Append("&").Append("marketDate=").Append(marketDate);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          List<PremarketElement> premarketData = JsonConvert.DeserializeObject<List<PremarketElement>>(json);
          return new ServiceResult(premarketData);
        }
        catch (Exception exception)
        {
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false, exception.ToString());
        }
      }
    }

    public ServiceResult GetAvailableMarkets()
    {
      lock (this)
      {
        try
        {
          if (!IsNetworkAvailable()) return new ServiceResult(false, "No network.");
          if (!IsAuthorized()) return new ServiceResult(false, "Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/PreMarket/GetAvailableMarkets?").Append("token=").Append(accessToken);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          List<String> availableMarkets = JsonConvert.DeserializeObject<List<String>>(json);
          return new ServiceResult(availableMarkets);
        }
        catch (Exception exception)
        {
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false, exception.ToString());
        }
      }
    }

    public ServiceResult GetAvailableMarketDates(String market)
    {
      lock (this)
      {
        try
        {
          if (!IsNetworkAvailable()) return new ServiceResult(false, "No network.");
          if (!IsAuthorized()) return new ServiceResult(false, "Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/PreMarket/GetAvailableMarketDates?").Append("token=").Append(accessToken).Append("&").Append("market=").Append(HttpUtility.UrlEncode(market));
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          List<DateTime> availableMarketDates = JsonConvert.DeserializeObject<List<DateTime>>(json);
          return new ServiceResult(availableMarketDates);
        }
        catch (Exception exception)
        {
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false, exception.ToString());
        }
      }
    }
// ************************************************************************************************************************************************
    public ServiceResult GetPortfolioTradesWithCombinedLots(String symbol)
    {
      lock(this)
      {
        try
        {
          if(!IsNetworkAvailable())return new ServiceResult(false,"No network.");
          if (!IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/Portfolio/GetPortfolioTradesWithCombinedLots?").Append("token=").Append(accessToken).Append("&").Append("symbol=").Append(symbol);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          List<PortfolioTrade> portfolioTrades= JsonConvert.DeserializeObject<List<PortfolioTrade>>(json);
          return new ServiceResult(portfolioTrades);
        }
        catch (Exception exception)
        {                                                                                     
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false,exception.ToString());
        }
      }
    }

    public ServiceResult GetStopLimit(String symbol)
    {
      lock(this)
      {
        try
        {
          if(!IsNetworkAvailable())return new ServiceResult(false,"No network.");
          if (!IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/Portfolio/GetStopLimit?").Append("token=").Append(accessToken).Append("&").Append("symbol=").Append(symbol);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          if(null==json)return new ServiceResult(null);
          StopLimit stopLimit= JsonConvert.DeserializeObject<StopLimit>(json);
          return new ServiceResult(stopLimit);
        }
        catch (Exception exception)
        {                                                                                     
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false,exception.ToString());
        }
      }
    }

    public ServiceResult GetPortfolioTradesWithParityPrice(String symbol)
    {
      lock(this)
      {
        try
        {
          if(!IsNetworkAvailable())return new ServiceResult(false,"No network.");
          if (!IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/Portfolio/GetPortfolioTradesWithParityPrice?").Append("token=").Append(accessToken).Append("&").Append("symbol=").Append(symbol);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          PortfolioTradesWithParityPrice portfolioTradesWithParityPrice= JsonConvert.DeserializeObject<PortfolioTradesWithParityPrice>(json);
          return new ServiceResult(portfolioTradesWithParityPrice);
        }
        catch (Exception exception)
        {                                                                                     
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false,exception.ToString());
        }
      }
    }

    public ServiceResult GetPositionsWithDescription()
    {
      lock (this)
      {
        try
        {
          if (!IsNetworkAvailable()) return new ServiceResult(false, "No network.");
          if (!IsAuthorized()) return new ServiceResult(false, "Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/Portfolio/GetOpenPositionsWithDescription?").Append("token=").Append(accessToken);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          List<PositionWithDescription> positionsWithDescription = JsonConvert.DeserializeObject<List<PositionWithDescription>>(json);
          return new ServiceResult(positionsWithDescription);
        }
        catch (Exception exception)
        {
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false, exception.ToString());
        }
      }
    }

    public ServiceResult GetWatchList(String watchList)
    {
      lock(this)
      {
        try
        {
          if(!IsNetworkAvailable())return new ServiceResult(false,"No network.");
          if (!IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/WatchList/GetWatchList?").Append("token=").Append(accessToken).Append("&").Append("watchList=").Append(watchList);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          List<String> symbols= JsonConvert.DeserializeObject<List<String>>(json);
          return new ServiceResult(symbols);
        }
        catch (Exception exception)
        {                                                                                     
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false,exception.ToString());
        }
      }
    }

    public ServiceResult GetHeadlines(DateTime headlineDate)
    {
      lock(this)
      {
        try
        {
          if(!IsNetworkAvailable())return new ServiceResult(false,"No network.");
          if (!IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/Headlines/GetHeadlines?").Append("token=").Append(accessToken).Append("&").Append("headlineDate=").Append(headlineDate);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          List<Headline> headlines= JsonConvert.DeserializeObject<List<Headline>>(json);
          return new ServiceResult(headlines);
        }
        catch (Exception exception)
        {                                                                                     
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false,exception.ToString());
        }
      }
    }

    public ServiceResult GetHeadlineDates()
    {
      lock(this)
      {
        try
        {
          if(!IsNetworkAvailable())return new ServiceResult(false,"No network.");
          if (!IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/Headlines/GetHeadlineDates?").Append("token=").Append(accessToken);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          List<String> headlineDates= JsonConvert.DeserializeObject<List<String>>(json);
          return new ServiceResult(headlineDates);
        }
        catch (Exception exception)
        {                                                                                     
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false,exception.ToString());
        }
      }
    }

    public ServiceResult GetLatestHeadlines()
    {
      lock(this)
      {
        try
        {
          if(!IsNetworkAvailable())return new ServiceResult(false,"No network.");
          if (!IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/Headlines/GetLatestHeadlines?").Append("token=").Append(accessToken);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          List<Headline> latestHeadlines= JsonConvert.DeserializeObject<List<Headline>>(json);
          return new ServiceResult(latestHeadlines);
        }
        catch (Exception exception)
        {                                                                                     
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false,exception.ToString());
        }
      }
    }

    public ServiceResult GetLatestPricingDate()
    {
      lock(this)
      {
        try
        {
          if(!IsNetworkAvailable())return new ServiceResult(false,"No network.");
          if (!IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/Price/GetLatestPricingDate?").Append("token=").Append(accessToken);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          DateTime latestPricingDate= JsonConvert.DeserializeObject<DateTime>(json);
          return new ServiceResult(latestPricingDate);
        }
        catch (Exception exception)
        {                                                                                     
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false,exception.ToString());
        }
      }
    }

    public ServiceResult GetGainLossDetails(DateTime selectedDate,String account)
    {
      lock(this)
      {
        try
        {
        if(!IsNetworkAvailable())return new ServiceResult(false,"No network.");
        if (!IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("/api/GainLoss/GetGainLossWithDetail?").Append("token=").Append(accessToken).Append("&selectedDate=").Append(selectedDate.ToShortDateString()).Append("&").Append("account=").Append(account);
        String json = httpClient.GetStringAsync(sb.ToString()).Result;
        List<GainLossSummaryItemDetail> gainLossSummaryDetail = JsonConvert.DeserializeObject<List<GainLossSummaryItemDetail>>(json);
        return new ServiceResult(gainLossSummaryDetail);
        }
        catch (Exception exception)
        {                                                                                     
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false,exception.ToString());
        }
      }
    }

    public ServiceResult GetGainLossDetails(DateTime selectedDate)
    {
      lock(this)
      {
        try
        {
          if(!IsNetworkAvailable())return new ServiceResult(false,"No network.");
          if (!IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/GainLoss/GetGainLossWithDetail?").Append("token=").Append(accessToken).Append("&selectedDate=").Append(selectedDate.ToShortDateString());
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          List<GainLossSummaryItemDetail> gainLossSummaryDetail = JsonConvert.DeserializeObject<List<GainLossSummaryItemDetail>>(json);
          return new ServiceResult(gainLossSummaryDetail);
        }
        catch (Exception exception)
        {
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false,exception.ToString());
        }
      }
    }

    public ServiceResult GetCompoundGainLoss(int selectedDays)
    {
      lock(this)
      {
        try
        {
          if(!IsNetworkAvailable())return new ServiceResult(false,"No network.");
          if (!IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
          StringBuilder sb = new StringBuilder();
          bool includeDividends=false;
          sb.Append("/api/GainLoss/GetCompoundGainLoss?").Append("token=").Append(accessToken).Append("&selectedDays=").Append(selectedDays).Append("&").Append("includeDividends=").Append(includeDividends);;
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          GainLossCompoundModelCollection gainLossCompoundModelCollection = JsonConvert.DeserializeObject<GainLossCompoundModelCollection>(json);
          return new ServiceResult(gainLossCompoundModelCollection);
        }
        catch (Exception exception)
        {
          exceptions.Add(exception);
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false,exception.ToString());
        }
      }
    }

    public ServiceResult GetAccountsWithOpenTrades()
    {
      lock(this)
      {
        try
        {
          if(!IsNetworkAvailable())return new ServiceResult(false,"No network.");
          if (!IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/Portfolio/GetAccountsWithOpenTrades?").Append("token=").Append(accessToken);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          List<String> accounts = JsonConvert.DeserializeObject<List<String>>(json);
          return new ServiceResult(accounts);
        }
        catch (Exception exception)
        {
          Debug.WriteLine(exception.ToString());
          return null;
        }
      }
    }
    public ServiceResult GetPrices(String symbol, int days)
    {
      lock(this)
      {
        try
        {
          if(!IsNetworkAvailable())return new ServiceResult(false,"No network.");
          if (!IsAuthorized())return new ServiceResult(false,"Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/Price/GetPrices?").Append("token=").Append(accessToken).Append("&").Append("symbol=").Append(symbol).Append("&").Append("days=").Append(days);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          List<Price> prices = JsonConvert.DeserializeObject<List<Price>>(json);
          return new ServiceResult(prices);
        }
        catch (Exception exception)
        {
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false,exception.ToString());
        }
      }
    }
    public ServiceResult GetCompanyNameForSymbol(String symbol)
    {
      lock(this)
      {
        try
        {
          if(!IsNetworkAvailable())return new ServiceResult(false,"No network.");
          if (!IsAuthorized())return new ServiceResult(false,"Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/Price/GetCompanyNameForSymbol?").Append("token=").Append(accessToken).Append("&").Append("symbol=").Append(symbol);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          String companyName = JsonConvert.DeserializeObject<String>(json);
          return new ServiceResult(companyName);
        }
        catch (Exception exception)
        {
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false,exception.ToString());
        }
      }
    }
    public ServiceResult GetBollingerBands(String symbol,int dayCount)
    {
      lock(this)
      {
        try
        {
          if(!IsNetworkAvailable())return new ServiceResult(false,"No network.");
          if (!IsAuthorized())return new ServiceResult(false,"Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("/api/Price/GetBollingerBands?").Append("token=").Append(accessToken).Append("&").Append("symbol=").Append(symbol).Append("&").Append("dayCount=").Append(dayCount);
          String json = httpClient.GetStringAsync(sb.ToString()).Result;
          List<BollingerBandElement> bollingerBands=JsonConvert.DeserializeObject<List<BollingerBandElement>>(json);
          return new ServiceResult(bollingerBands);
        }
        catch (Exception exception)
        {
          Debug.WriteLine(exception.ToString());
          return new ServiceResult(false,exception.ToString());
        }
      }
    }
    public bool IsAuthorized()
    {
      if (null == accessToken) return false;
      return true;
    }
    public static bool IsNetworkAvailable()
    {
      var current = Connectivity.NetworkAccess;
      if (current == NetworkAccess.Internet)return true;
      return false;
    }
    public static bool IsWiFiNetwork()
    {
      if(!IsNetworkAvailable())return false;
      if(Connectivity.ConnectionProfiles.Contains(ConnectionProfile.WiFi))return true;
      return false;
    }

  }
}
