using System;
using System.IO;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Web;

/// Version 1.14 - 2017-03-17

/// <summary>
/// Summary description for Class1
/// </summary>
public class srFax
{

    private string access_id;
    private string access_pwd;
    private string serverUrl = "https://secure.srfax.com/SRF_SecWebSvc.php";

    private bool lastStatus;
    private string lastResponse;

    public srFax(string id, string pwd)
    {
        access_id = id;
        access_pwd = pwd;
    }


    public bool Queue_Fax(Dictionary<string, string> parameters)
    {
        string[] requiredFields = { "sCallerID", "sSenderEmail", "sFaxType", "sToFaxNumber" };
        string[] optionalFields = {"sResponseFormat", "sAccountCode", "sRetries", "sCoverPage", "sCPFromName", "sCPToName", "sCPOrganization",
                                   "sCPSubject", "sCPComments", "sFileName_*", "sFileContent_*", "sNotifyURL", "sFaxFromHeader", "sQueueFaxDate", "sQueueFaxTime" };

        _validateRequiredVariables(requiredFields, parameters);

        Dictionary<string, string> postVariables = _preparePostVariables(requiredFields, optionalFields, parameters);

        postVariables.Add("action", "Queue_Fax");
        postVariables.Add("access_id", access_id);
        postVariables.Add("access_pwd", access_pwd);

        string result = _processRequest(postVariables);

        _processResponse(result);

        return lastStatus ;
        
    }

    public bool Get_FaxStatus(Dictionary<string, string> parameters)
    {
        string[] requiredFields = { "sFaxDetailsID" };
        string[] optionalFields = { "sResponseFormat" };

        _validateRequiredVariables(requiredFields, parameters);

        Dictionary<string, string> postVariables = _preparePostVariables(requiredFields, optionalFields, parameters);

        postVariables.Add("action", "Get_FaxStatus");
        postVariables.Add("access_id", access_id);
        postVariables.Add("access_pwd", access_pwd);

        string result = _processRequest(postVariables);

        _processResponse(result);

        return lastStatus;
    }

    public bool Get_MultiFaxStatus(Dictionary<string, string> parameters)
    {
        string[] requiredFields = { "sFaxDetailsID" };
        string[] optionalFields = { "sResponseFormat" };

        _validateRequiredVariables(requiredFields, parameters);

        Dictionary<string, string> postVariables = _preparePostVariables(requiredFields, optionalFields, parameters);

        postVariables.Add("action", "Get_MultiFaxStatus");
        postVariables.Add("access_id", access_id);
        postVariables.Add("access_pwd", access_pwd);

        string result = _processRequest(postVariables);

        _processResponse(result);

        return lastStatus;
    }


    public bool Get_Fax_Inbox(Dictionary<string, string> parameters)
    {

        string[] requiredFields = { };
        string[] optionalFields = {"sResponseFormat", "sPeriod", "sStartDate", "sEndDate", "sViewedStatus", "sIncludeSubUsers", "sFaxDetailsID" };


        Dictionary<string, string> postVariables = _preparePostVariables(requiredFields, optionalFields, parameters);

        postVariables.Add("action", "Get_Fax_Inbox");
        postVariables.Add("access_id", access_id);
        postVariables.Add("access_pwd", access_pwd);

        string result = _processRequest(postVariables);

        _processResponse(result);

        return lastStatus;

    }

    public bool Get_Fax_Outbox(Dictionary<string, string> parameters)
    {

        string[] requiredFields = { };
        string[] optionalFields = { "sResponseFormat", "sPeriod", "sStartDate", "sEndDate", "sIncludeSubUsers", "sFaxDetailsID" };


        Dictionary<string, string> postVariables = _preparePostVariables(requiredFields, optionalFields, parameters);

        postVariables.Add("action", "Get_Fax_Outbox");
        postVariables.Add("access_id", access_id);
        postVariables.Add("access_pwd", access_pwd);

        string result = _processRequest(postVariables);

        _processResponse(result);

        return lastStatus;

    }

    public bool Retrieve_Fax(Dictionary<string, string> parameters)
    {
        string[] requiredFields = { "sFaxFileName|sFaxDetailsID", "sDirection" };
        string[] optionalFields = { "sFaxFormat", "sMarkasViewed", "sResponseFormat", "sSubUserID" };

        _validateRequiredVariables(requiredFields, parameters);

        Dictionary<string, string> postVariables = _preparePostVariables(requiredFields, optionalFields, parameters);

        postVariables.Add("action", "Retrieve_Fax");
        postVariables.Add("access_id", access_id);
        postVariables.Add("access_pwd", access_pwd);

        string result = _processRequest(postVariables);

        _processResponse(result);

        return lastStatus;
    }

    public bool Update_Viewed_Status(Dictionary<string, string> parameters)
    {
        string[] requiredFields = { "sFaxFileName|sFaxDetailsID", "sDirection", "sMarkasViewed" };
        string[] optionalFields = { "sResponseFormat" };

        _validateRequiredVariables(requiredFields, parameters);

        Dictionary<string, string> postVariables = _preparePostVariables(requiredFields, optionalFields, parameters);

        postVariables.Add("action", "Update_Viewed_Status");
        postVariables.Add("access_id", access_id);
        postVariables.Add("access_pwd", access_pwd);

        string result = _processRequest(postVariables);

        _processResponse(result);

        return lastStatus;

    }

    public bool Delete_Fax(Dictionary<string, string> parameters)
    {
        string[] requiredFields = { "sDirection", "sFaxFileName_*|sFaxDetailsID_*" };
        string[] optionalFields = { "sResponseFormat" };

        _validateRequiredVariables(requiredFields, parameters);

        Dictionary<string, string> postVariables = _preparePostVariables(requiredFields, optionalFields, parameters);

        postVariables.Add("action", "Delete_Fax");
        postVariables.Add("access_id", access_id);
        postVariables.Add("access_pwd", access_pwd);

        string result = _processRequest(postVariables);

        _processResponse(result);

        return lastStatus;

    }

    public bool Stop_Fax(Dictionary<string, string> parameters)
    {
        string[] requiredFields = { "sFaxDetailsID"};
        string[] optionalFields = { "sResponseFormat" };

        _validateRequiredVariables(requiredFields, parameters);

        Dictionary<string, string> postVariables = _preparePostVariables(requiredFields, optionalFields, parameters);

        postVariables.Add("action", "Stop_Fax");
        postVariables.Add("access_id", access_id);
        postVariables.Add("access_pwd", access_pwd);

        string result = _processRequest(postVariables);

        _processResponse(result);

        return lastStatus;

    }

    public bool Get_Fax_Usage(Dictionary<string, string> parameters)
    {
        string[] requiredFields = { };
        string[] optionalFields = { "sResponseFormat", "sPeriod", "sStartDate", "sEndDate", "sIncludeSubUsers" };

        _validateRequiredVariables(requiredFields, parameters);

        Dictionary<string, string> postVariables = _preparePostVariables(requiredFields, optionalFields, parameters);

        postVariables.Add("action", "Get_Fax_Usage");
        postVariables.Add("access_id", access_id);
        postVariables.Add("access_pwd", access_pwd);

        string result = _processRequest(postVariables);

        _processResponse(result);

        return lastStatus;

    }

    public string Get_Last_Response()
    {
        return lastResponse;
    }

    public bool Get_Last_Status()
    {
        return lastStatus;
    }
  
    /*******************INTERNAL FUNCTIONS*********************************/


    private void _processResponse(string response)
    {
        if (response.IndexOf("Success") != -1)
        {
            lastStatus = true;
        }
        else
        {
            lastStatus = false;
        }

        lastResponse = response;

        return;

    }

    private string _processRequest(Dictionary<string, string> postVariables)
    {
        string queryString = _prepareQueryString(postVariables);
        string result      = "";
        using (WebClient wc = new WebClient()) {
			
			ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
			
            wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
            result = wc.UploadString(serverUrl, queryString);
        }

        return result;
    }


    private string _prepareQueryString(Dictionary<string, string> postVariables)
    {
        string queryString = "";

        foreach (KeyValuePair<string, string> entry in postVariables)
        {
            queryString += entry.Key.ToString() + "=" + UrlEncode(entry.Value.ToString()) + "&";
        }

        // remove last &
        queryString = queryString.Remove(queryString.Length - 1);

        return queryString;

    }

    public static string UrlEncode(string str)

    {

        StringBuilder sb = new StringBuilder();

        byte[] byStr = System.Text.Encoding.UTF8.GetBytes(str);

        for (int i = 0; i < byStr.Length; i++)

        {

             sb.Append(@"%" + byStr[i].ToString("X2"));

        }

        return (sb.ToString());

    }
    
    private Dictionary<string, string> _preparePostVariables(string[] requiredFields, string[] optionalFields, Dictionary<string, string> parameters)
    {
        Dictionary<string, string> postVariables = new Dictionary<string, string>();

        List<string> list = new List<string>();
        list.AddRange(requiredFields);
        list.AddRange(optionalFields);

        string[] inputVariables = list.ToArray();


        foreach (string field in inputVariables)
        {
            if (field.EndsWith("*") && field.IndexOf('|') == -1) // non-piped wildcard
            {
                string fieldPrefix = field.Replace("*", "");
                Dictionary<string, string> wildCards = _getWildcardVaribles(fieldPrefix, parameters);
                postVariables = _mergeDictionaries(postVariables, wildCards);

            }
            else
            {
                if (field.IndexOf('|') != -1) // piped, non-wildcard
                {
                    string[] pipedFields = field.Split('|');

                    foreach (string pipedField in pipedFields)
                    {
                        if (pipedField.EndsWith("*")) // piped wildcard
                        {
                            string fieldPrefix = pipedField.Replace("*", "");
                            Dictionary<string, string> wildCards = _getWildcardVaribles(fieldPrefix, parameters);
                            postVariables = _mergeDictionaries(postVariables, wildCards);
                        }
                        else
                        {
                            if (parameters.ContainsKey(pipedField))
                            {
                                string value = parameters[pipedField];
                                if (value.Length > 0)
                                {
                                    postVariables.Add(pipedField, value);
                                }
                            }
                        }
                    }


                }
                else //non-special fieldname
                {
                    if (parameters.ContainsKey(field))
                    {
                        postVariables.Add(field, parameters[field]);
                    }
                }
            }

        }


        return postVariables;
    }


    private Dictionary<string, string> _getWildcardVaribles(string fieldPrefix, Dictionary<string, string> parameters)
    {
        Dictionary<string, string> wildCards = new Dictionary<string, string>();
        bool done = false;
        int suffix = 1;

        while (!done)
        {
            string field = fieldPrefix + suffix;

            if (parameters.ContainsKey(field))
            {
                string value = parameters[field];

                if (value.Length > 0) // add variable to the collection
                {
                    wildCards.Add(field, value);
                }
                else // field value is empty, so finish
                {
                    done = true;
                }
            }
            else
            {
                done = true;
            }

            suffix++;

            // fail safe to ensure no infinite loops
            if (suffix > 1000)
            {
                done = true;
            }

        }

        return wildCards;

    }

    private void _validateRequiredVariables(string[] requiredVariables, Dictionary<string, string> parameters)
    {

        foreach (string field in requiredVariables)
        {

            string error = "";

            if (field.EndsWith("*") && field.IndexOf("|") == -1) // non piped wildcard variable.  check for first instance
            {
                string fieldPrefix = field.Replace("*", "");
                string wildCard = fieldPrefix + "1";

                if (!parameters.ContainsKey(wildCard))
                {
                    error = "Required Field missing.  No values for " + fieldPrefix;
                }
                else
                {
                    string value = parameters[wildCard];
                    if (value.Length <= 0)
                    {
                        error = "Required Field missing.  No values for " + fieldPrefix;
                    }
                }


            }
            else
            {

                if (field.IndexOf("|") != -1) // piped separated variable.  At lease 1 must be present.
                {
                    string[] pipedFields = field.Split('|');
                    bool checkSuccessful = false;

                    foreach (string pipedField in pipedFields)
                    {
                        string trimmedPipedField = pipedField.Trim();

                        if (trimmedPipedField.EndsWith("*")) // piped value has a wildcard, look for first value
                        {
                            string prefix = trimmedPipedField.Replace("*", "");
                            string wildcard = prefix + "1";

                            if (parameters.ContainsKey(wildcard)) // parameter exists, check to make sure it has a value
                            {
                                string pVal = parameters[wildcard];
                                if (pVal.Length > 0)
                                {
                                    checkSuccessful = true;
                                }

                            }

                        }
                        else
                        {
                            if (parameters.ContainsKey(trimmedPipedField))
                            {
                                string pVal = parameters[trimmedPipedField];
                                if (pVal.Length > 0)
                                {
                                    checkSuccessful = true;
                                }
                            }
                        }
                    }

                    if (!checkSuccessful)
                    {

                        error = "Required field missing.  You must provide at lease 1 of the following: " + string.Join(",", pipedFields);
                    }

                }
                else // standard field, check if it exists
                {

                    if (!parameters.ContainsKey(field))
                    {
                        error = "Required field " + field + " is missing!";
                    }
                    else // ensure field value is not empty
                    {
                        string value = parameters[field];
                        if (value.Length <= 0)
                        {
                            error = "Required field " + field + " is missing!";
                        }
                    }

                }
            }


            if (error.Length > 0)
            {
                throw (new Exception(error));
                return;
            }
        }



        return;
    }

    // merges 2 dictionaries into one, stops duplicates
    private Dictionary<string, string> _mergeDictionaries(Dictionary<string, string> d1, Dictionary<string, string> d2)
    {
        Dictionary<string, string> mergedDictionary = new Dictionary<string, string>();

        foreach (KeyValuePair<string, string> entry in d1)
        {
            if (!mergedDictionary.ContainsKey(entry.Key))
            {
                mergedDictionary.Add(entry.Key, entry.Value);
            }
        }

        foreach (KeyValuePair<string, string> entry in d2)
        {
            if (!mergedDictionary.ContainsKey(entry.Key))
            {
                mergedDictionary.Add(entry.Key, entry.Value);
            }
        }

        return mergedDictionary;
    }


}
