using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

// Add these for K2 functionality with Client API
using SourceCode.Hosting.Client.BaseAPI;
using SourceCode.SmartObjects.Client;
using SourceCode.Workflow.Client;
using System.Security.Principal;

namespace ExternalSystemSample
{
    public partial class Form1 : Form
    {
        // Create a New HttpClient object as static for the application to make REST calls, make sure it's only created once to avoid opening multiple ports on a server, per application.
        // Per Microsoft Remarks section on this page: https://msdn.microsoft.com/en-us/library/system.net.http.httpclient(v=vs.110).aspx
        static HttpClient k2CloudClient;


        #region NOT FOR CODE REFERENCE, USE IN THIS DEMO APPLICATION ONLY

        public Form1()
        {
            InitializeComponent();
        }


        private void Form1_Load(object sender, EventArgs e)
        {

            string restUserName = ConfigurationManager.AppSettings["restUserName"];
            string restPassword = ConfigurationManager.AppSettings["restPassword"];

            // Providing static credentials for this demo, using a client handler to store credentials. 
            // Consider using stronger security for this in applications.
            NetworkCredential k2Credentials = new NetworkCredential(restUserName, restPassword);
            HttpClientHandler loginHandler = new HttpClientHandler { Credentials = k2Credentials };

            // Open the HTTPClient connection one time upon form load so that we only open one port/socket on the server when making a REST call.
            k2CloudClient = new HttpClient(loginHandler);

            GetPostedK2ItemsList();
        }


        /// <summary>
        /// Make btnPostREST and btnPostAPI post buttons visible if the Status column of the selected row is set to the "waiting" state.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            var senderGrid = (DataGridView)sender;

            if (e.RowIndex >= 0)
            {
                if (senderGrid.CurrentRow.Cells["Status"].Value.ToString() == "Waiting")
                {
                    btnPostAPI.Visible = true;
                    btnPostREST.Visible = true;
                }
                else
                {
                    btnPostAPI.Visible = false;
                    btnPostREST.Visible = false;
                }

            }
        }


        private void btnPostREST_Click(object sender, EventArgs e)
        {
            string appResponse;
            string baseURL = ConfigurationManager.AppSettings["baseURL"];

            appResponse = GetSelectedResponse();
            FinishK2StepbyREST(dataGridView1.CurrentRow.Cells["StepSerialNumber"].Value.ToString(), appResponse, baseURL);

            btnPostAPI.Visible = false;
            btnPostREST.Visible = false;
        }


        private void btnPostAPI_Click(object sender, EventArgs e)
        {
            string appResponse;

            // Get the connection string for using the K2 Client API      
            SCConnectionStringBuilder csbHostServer = GetConnectionStringBuilderByName("K2ServerConnectionAPI");

            appResponse = GetSelectedResponse();
            FinishK2StepbyAPI(dataGridView1.CurrentRow.Cells["StepSerialNumber"].Value.ToString(), appResponse, csbHostServer.Host);

            btnPostAPI.Visible = false;
            btnPostREST.Visible = false;
        }


        private void btnRefresh_Click(object sender, EventArgs e)
        {
            GetPostedK2ItemsList();
        }


        /// <summary>
        /// NOT FOR CODE REFERENCE, USE IN THIS DEMO APPLICATION ONLY
        /// </summary>
        private void GetPostedK2ItemsList()
        {
            SCConnectionStringBuilder csbHostServer = GetConnectionStringBuilderByName("K2ServerConnectionAPI");

            //Open the connection to K2
            SourceCode.SmartObjects.Client.SmartObjectClientServer soServer = new SmartObjectClientServer();
            soServer.CreateConnection();
            soServer.Connection.Open(csbHostServer.ToString());

            // get an object representing the SmartObject definition so we can set properties or interrogate the SmartObject definition
            SmartObject mySmartObject = soServer.GetSmartObject("External_System_SmartObject");

            mySmartObject.MethodToExecute = "GetList";
            dataGridView1.DataSource = soServer.ExecuteListDataTable(mySmartObject);

            // close the K2 connection
            soServer.Connection.Close();
        }


        /// <summary>
        /// Get the selected radiobutton from the form. Possible values are "Success" or "Fail".
        /// NOT FOR CODE REFERENCE, USE IN THIS DEMO APPLICATION ONLY
        /// </summary>
        /// <returns> "Success" or "Fail" string from the text property of a checked radiobuttion. </returns>
        private string GetSelectedResponse()
        {
            // Linq search GroupBox control for checked(selected) radio button. Use selected radiobutton's text as application's response.
            RadioButton selectedButton = gboxRadioButtons.Controls.OfType<RadioButton>().FirstOrDefault(r => r.Checked);
            return selectedButton.Text;
        }


        /// <summary>
        /// Retrieve connection string from the app.config file that points to the K2 Server.
        /// </summary>
        /// <param name="name">Name of the connection string via the key field in the app.config connectionStrings section.</param>
        /// <returns></returns>
        private SCConnectionStringBuilder GetConnectionStringBuilderByName(string name)
        {
            // Use connection string defined in App.config file with the SCConnectionStringBuilder
            SCConnectionStringBuilder connectionStringBuilder = new SCConnectionStringBuilder();
            connectionStringBuilder.ConnectionString = ConfigurationManager.ConnectionStrings[name].ConnectionString;

            return connectionStringBuilder;
        }


        #endregion


        #region FOR USE IN CODE REFERENCE


        /// <summary>
        /// This method uses the K2 Client api with a ServerItem object to finish an Asynchronous SmartObject event. 
        /// NOTE: it also returns a "Success" or "Fail" message back to the K2 workflow through the datafield called "ExternalAppResponse". 
        /// </summary>
        /// <param name="stepSerialNumber">Serial number of the K2 step waiting on this external application to send complete notice. This value can come into your external 
        /// application by polling a SmartObject datasource, or whatever method you use to pull in external data.
        /// </param>
        /// <param name="appResponse">Pass in a response with this parameter to send back to K2 using a workflow defined variable named "ExternalAppResponse". Values will either be "Success" or "Fail"</param>
        /// <remarks>TODO: Ensure external application is running under an account with access to complete the workflow step in K2.</remarks>
        private void FinishK2StepbyAPI(string stepSerialNumber, string appResponse, string host)
        {
            using (SourceCode.Workflow.Client.Connection K2Conn = new Connection())
            {
                try
                {
                    K2Conn.Open(host);

                    // Use the ServerItem object to open the SmartObject step that is waiting in the workflow. Do this by passing in the step serial number.
                    ServerItem svrItem = K2Conn.OpenServerItem(stepSerialNumber);

                    // Pass datafield values back to K2, if necessary. In this example, we're passing back a response from this external application of "Success" or "Fail"
                    // through a datafield called "ExternalAppResponse", which corresponds to a variable defined in the running instance of the K2 workflow.
                    svrItem.ProcessInstance.DataFields["ExternalAppResponse"].Value = appResponse;

                    // Finish the server item to tell the workflow to continue
                    svrItem.Finish();
                }
                catch (Exception ex)
                {
                    // Handle errors accordingly.
                }
            }
        }


        /// <summary>
        /// This method uses the serverEvents REST endpoint to finish an Asynchronous SmartObject event. Include the Step serial number within the 
        /// "finish" endpoint path.
        /// e.g. https://[K2 SERVER DOMAIN]/api/workflow/[VERSION]/serverEvents/" + stepSerialNumber + "/finish"
        ///    - [VERSION] is the REST API version, which may be something like "v1" or "preview". This is found in the K2 Management site under the REST 
        ///    integration settings.
        ///    
        /// NOTE: it also returns a "Success" or "Fail" message back to the K2 workflow through the datafield called "ExternalAppResponse" using JSON.
        /// </summary>
        /// <param name="stepSerialNumber">Serial number of the K2 workflow step(SmartObject Event) that is waiting on this external application to finish. Use this in the 
        /// URL path for this REST endpoint.</param>        
        /// <param name="appResponse">Pass in a response with this parameter to send back to K2 using a workflow defined variable named "ExternalAppResponse". Values will either be "Success" or "Fail"</param>
        /// <param name="baseURL">Base URL for the REST endpoint. Build the full REST endpoint URI with either V1 or Preview version of the REST serverEvents endpoints.</param>
        /// 
        protected async void FinishK2StepbyREST(string stepSerialNumber, string appResponse, string baseURL)
        {
            HttpResponseMessage rm;

            // Build the JSON string to send back datafields with data, if necessary. In this example, we're passing back a response from this external application of "Success" or "Fail"
            // through a datafield called "ExternalAppResponse", which corresponds to a variable defined in the running instance of the K2 workflow.
            string userDefinedFields = "{\"dataFields\": { \"ExternalAppResponse\": \"" + appResponse + "\"}}";

         
            // Example: "https://[K2 SERVER DOMAIN]/api/workflow/preview/serverEvents/" + stepSerialNumber + "/finish";
            string requestUri = baseURL + stepSerialNumber + "/finish";

            System.Net.Http.StringContent httpContent = new StringContent(userDefinedFields, Encoding.UTF8, "application/json");

            try
            {
                rm = await k2CloudClient.PostAsync(requestUri, httpContent);

                if (rm.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    // Unauthorized user account making the event Finish call. 
                    // TODO: Ensure external application is running under an account with access to complete the workflow step in K2.
                }
                else if (rm.StatusCode == System.Net.HttpStatusCode.BadRequest)
                {
                    // Could be badly formed JSON string with datafield sent back to K2.
                    // TODO: Ensure datafields sent back, exist as variables in the K2 Workflow.
                }
            }
            catch (Exception ex)
            {
                // handle errors accordingly
            }
        }

        #endregion

        private void gboxRadioButtons_Enter(object sender, EventArgs e)
        {

        }
    }
}
