using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ServiceModel;
using System.Collections.ObjectModel;
using System.Web;
using System.IO;
using System.Collections;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

namespace CommunicationSupport
{
    internal static class ServiceUtility
    {
        public static object[] AssignParameters(HttpContext context,Type messageBodyClassType, Dictionary<string, ParameterInfo> methodParms, Collection<Type> knownTypes)
        {

            DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(messageBodyClassType, knownTypes);
            object jsonMessageBodyClass = jsonSerializer.ReadObject(context.Request.InputStream);

            object[] assignedParms = new object[methodParms.Count];
            int i= 0;
            ParameterInfo parmInfo;
            PropertyInfo propertyInfo;

            foreach (KeyValuePair<string, ParameterInfo> parmInfoDictionary in methodParms)
            {
                parmInfo = parmInfoDictionary.Value;
                propertyInfo = jsonMessageBodyClass.GetType().GetProperty(parmInfo.Name);
                assignedParms[i] = propertyInfo.GetValue(jsonMessageBodyClass, null);
                i++;
            }

            return assignedParms;

        }
        
  
        public static void WriteJsonToResponseStream(HttpContext context, object data, Type dataType, Collection<Type> knownTypes)
        {
            // przykładowa odpowiedź
            // {"d":{"__type":"Customer:#AjaxWebServicesDemo","Address":"C","City":"D","CustomerId":"0323e205-8561-4c62-8a0f-9e531c214e15","FirstName":"a","LastName":"B","State":"E","Zip":"F"}}
            StringBuilder ajaxAdditions = new StringBuilder();
            
            ajaxAdditions.Append("{");
            ajaxAdditions.Append("\"");
            ajaxAdditions.Append("d");
            ajaxAdditions.Append("\"");

            // sprawdza dane pod kątem zawierania wartości null
            string json = ConvertObjectToAjaxStyleString(data, dataType, knownTypes);
            if (json == "null")
            {
                ajaxAdditions.Append(":");
                ajaxAdditions.Append(json);
            }
            else
            {
                ajaxAdditions.Append(":{");
                ajaxAdditions.Append("\"");
                ajaxAdditions.Append("__type");
                ajaxAdditions.Append("\"");
                ajaxAdditions.Append(":");
                ajaxAdditions.Append("\"");
                ajaxAdditions.Append(dataType.Name);
                ajaxAdditions.Append(":");
                ajaxAdditions.Append("#");
                ajaxAdditions.Append(dataType.Namespace);
                ajaxAdditions.Append("\"");
                ajaxAdditions.Append(",");
                ajaxAdditions.Append(json);
            }
            ajaxAdditions.Append("}");

            context.Response.AddHeader("Content-Type", "application/json");
            context.Response.Output.Write(ajaxAdditions.ToString());
        }

        static string ConvertObjectToAjaxStyleString(object data, Type dataType, Collection<Type> knownTypes)
        {
            DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(dataType, knownTypes);
            MemoryStream memoryStream = new MemoryStream();
            jsonSerializer.WriteObject(memoryStream, data);
            string json = Encoding.GetEncoding("iso-8859-1").GetString(memoryStream.ToArray());
            return FormatJsonToAjaxStyle(json);
        }

        static string FormatJsonToAjaxStyle(string json)
        {
            // w razie pustego łańcucha nasze możliwości są bardzo ograniczone
            if (json.Length == 0)
                return json;

            int index = json.IndexOf("{");
            if (index < 0)
            {
                // nie stosujemy formatu {}
                return json;
            }
            else
            {
                // usuwamy początek, aby dostosować notację JSON do foramtu Ajax
                return json.Substring(index + 1);
            }
        }

        public static void WriteException(HttpContext context, Exception ex, int statusCode)
        {
            context.Response.ClearHeaders();
            context.Response.ClearContent();
            context.Response.Clear();
            context.Response.StatusCode = statusCode;
            context.Response.StatusDescription = HttpWorkerRequest.GetStatusDescription(statusCode);
            context.Response.ContentType = "application/json";
            context.Response.AddHeader("jsonerror", "true");

            StringBuilder ajaxAdditions = new StringBuilder();
            
            ajaxAdditions.Append("{");
            ajaxAdditions.Append("\"");
            ajaxAdditions.Append("d");
            ajaxAdditions.Append("\"");

            ajaxAdditions.Append(":");
            ajaxAdditions.Append(CreateExceptionString(ex));

            context.Response.Write(ajaxAdditions.ToString());

        }
        static string CreateExceptionString(Exception ex)
        {
            StringBuilder errorStack = new StringBuilder();
            errorStack.Append("W trakcie przetwarzania wystąpił błąd; wygenerowano następujący komunikat i ślad stosu:\n");
            errorStack.Append("Komunikat o błędzie: ");
            errorStack.Append(ex.Message);
            errorStack.Append("\n");
            errorStack.Append("Źródło błędu: ");
            errorStack.Append(ex.Source);
            errorStack.Append("Ślad stosu: ");
            errorStack.Append(ex.StackTrace);
            errorStack.Append("\n");
            Exception ex1 = ex.InnerException;
            while (ex1 != null)
            {
                errorStack.Append("Zagnieżdżony komunikat o błędzie: ");
                errorStack.Append(ex.Message);
                errorStack.Append("\n");
                errorStack.Append("Zagnieżdżone źródło błędu: ");
                errorStack.Append(ex.Source);
                errorStack.Append("Zagnieżdżony ślad stosu: ");
                errorStack.Append(ex.StackTrace);
                errorStack.Append("\n");
                ex1 = ex1.InnerException;
            }

            return errorStack.ToString();
        }

    }
}
