namespace SqlDynamite.Common

open System
open System.Collections.Generic
open System.Diagnostics
open System.Runtime.CompilerServices
open System.Text

type ConnectionType =
    | NET
    | ODBC
    | OLEDB

    override this.ToString() =
        match this with
        | ConnectionType.NET -> ".NET"
        | ConnectionType.ODBC -> "ODBC"
        | ConnectionType.OLEDB -> "OLEDB"

type ObjectType =
    | PROCEDURE
    | EXTENDED_PROCEDURE
    | VIEW
    | TABLE
    | FUNCTION
    | EXTENDED_FUNCTION
    | WINDOW_FUNCTION
    | AGGREGATE
    | TRIGGER
    | EXTENDED_TRIGGER
    | PRIMARY_KEY
    | FOREIGN_KEY
    | INDEX
    | SEQUENCE
    | PACKAGE
    | SYNONYM
    | TYPE
    | JOB
    | REPORT
    | STRING
    | LIST
    | HASH
    | SET
    | ZSET
    | STREAM
    | SERVICE
    | SERVICE_QUEUE
    | SERVICE_MESSAGE_TYPE
    | SERVICE_CONTRACT
    | ROUTE
    | REMOTE_SERVICE_BINDING
    | SERVICE_BROKER_PRIORITY
    | COMPUTED_COLUMN
    | LABEL
    | MEASUREMENT
    | CUBE
    | DIMENSION
    | OTHER

    override this.ToString() =
        match this with
        | ObjectType.INDEX -> "INDEX"
        | ObjectType.TABLE -> "TABLE"
        | ObjectType.PROCEDURE -> "PROCEDURE"
        | ObjectType.EXTENDED_PROCEDURE -> "EXTENDED PROCEDURE"
        | ObjectType.FUNCTION -> "FUNCTION"
        | ObjectType.EXTENDED_FUNCTION -> "EXTENDED FUNCTION"
        | ObjectType.WINDOW_FUNCTION -> "WINDOW FUNCTION"
        | ObjectType.AGGREGATE -> "AGGREGATE"
        | ObjectType.TRIGGER -> "TRIGGER"
        | ObjectType.EXTENDED_TRIGGER -> "EXTENDED TRIGGER"
        | ObjectType.VIEW -> "VIEW"
        | ObjectType.FOREIGN_KEY -> "FOREIGN KEY"
        | ObjectType.PRIMARY_KEY -> "PRIMARY KEY"
        | ObjectType.SEQUENCE -> "SEQUENCE"
        | ObjectType.PACKAGE -> "PACKAGE"
        | ObjectType.SYNONYM -> "SYNONYM"
        | ObjectType.TYPE -> "TYPE"
        | ObjectType.JOB -> "JOB"
        | ObjectType.REPORT -> "REPORT"
        | ObjectType.STRING -> "STRING"
        | ObjectType.LIST -> "LIST"
        | ObjectType.HASH -> "HASH"
        | ObjectType.SET -> "SET"
        | ObjectType.ZSET -> "SORTED SET"
        | ObjectType.STREAM -> "STREAM"
        | ObjectType.SERVICE -> "SERVICE"
        | ObjectType.SERVICE_QUEUE -> "SERVICE QUEUE"
        | ObjectType.SERVICE_MESSAGE_TYPE -> "SERVICE MESSAGE TYPE"
        | ObjectType.SERVICE_CONTRACT -> "SERVICE CONTRACT"
        | ObjectType.ROUTE -> "ROUTE"
        | ObjectType.REMOTE_SERVICE_BINDING -> "REMOTE SERVICE BINDING"
        | ObjectType.SERVICE_BROKER_PRIORITY -> "SERVICE BROKER PRIORITY"
        | ObjectType.LABEL -> "LABEL"
        | ObjectType.MEASUREMENT -> "MEASUREMENT"
        | ObjectType.CUBE -> "CUBE"
        | ObjectType.DIMENSION -> "DIMENSION"
        | _ -> "OTHER"

[<Extension>]
type Extensions() =
    [<Extension>] 
    static member GetExceptionMessages(exc: Exception) =
        let result = StringBuilder()
        let rec loop (exc:Exception) =
            if not (exc.Message = "Exception has been thrown by the target of an invocation.") then
                ignore(result.AppendLine(exc.Message))
            if not (exc.InnerException = null) then
                loop exc.InnerException
            else
                ()
        match exc with
        | :? AggregateException as aggExc ->
            for innerExc in aggExc.InnerExceptions do
                loop(innerExc)
        | _ ->
            loop exc
        result.ToString()
        
type ConsoleWorker() =
    let mutable _log:string = ""

    let prc_OutputReadLine(sender:obj) (e:DataReceivedEventArgs) =
        if e.Data <> null then _log <- _log + String.Format("{0}\n", e.Data)

    let prc_ErrorReadLine(sender:obj) (e:DataReceivedEventArgs) =
        if e.Data <> null then _log <- _log + String.Format("ERROR: {0}\n", e.Data)

    member this.StartConsoleProgram(prg:string) (prm:string) : string = 
        let pi = ProcessStartInfo(prg, prm)
        pi.CreateNoWindow <- true
        pi.RedirectStandardError <- true
        pi.RedirectStandardOutput <- true
        pi.UseShellExecute <- false
        try
            let prc = Process.Start(pi)
            prc.OutputDataReceived.AddHandler(DataReceivedEventHandler(prc_OutputReadLine))
            prc.ErrorDataReceived.AddHandler(DataReceivedEventHandler(prc_ErrorReadLine))
            prc.BeginOutputReadLine()
            prc.BeginErrorReadLine()
            let rec wait() =
                if prc.WaitForExit(1000) = false then wait()
            wait()
        with
            | _exc -> ()
        _log

type TupleComparer() =
    let first t =
        let a,_,_ = t
        a
    
    let second t =
        let _,b,_ = t
        b
    
    interface IComparer<string * ObjectType * string> with
        member this.Compare(x:string * ObjectType * string, y:string * ObjectType * string) : int =
            let result = (second x).ToString().CompareTo((second y).ToString())
            if result = 0 then
                (first x).CompareTo(first y)
            else
                result
