/**
* Licensed Materials - Property of IBM* and/or HCL**
* IBM UrbanCode Deploy
* (c) Copyright IBM Corporation 2002, 2017. All Rights Reserved.
* (c) Copyright HCL Technologies Ltd. 2018. All Rights Reserved.
*
* U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
* GSA ADP Schedule Contract with IBM Corp.
*
* * Trademark of International Business Machines
* ** Trademark of HCL Technologies Limited
*/

package com.urbancode.air.plugin.f5

import iControl.LocalLBClassAddressClass
import iControl.LocalLBClassAddressEntry
import iControl.LocalLBClassFileFormatType
import iControl.LocalLBClassFileModeType
import iControl.LocalLBClassMetaInformation
import iControl.LocalLBClassStringClass
import iControl.LocalLBClassValueClass

// Note: F5 uses Class and Data Group interchangeably
public class F5Class extends F5Server{
    Map<String,String> props

    // Constructor for F5Class
    public F5Class(Map<String,String> props) {
        super(props)
        this.props = props
    }

    // Retrieves a Address list of all Classes in the given partition
    public String[] getAddressClassList() {
        return getConnection().getLocalLBClass().get_address_class_list()
    }

    // Retrieves a ExternalV2 list of all Classes in the given partition
    public String[] getExternalClassList() {
        return getConnection().getLocalLBClass().get_external_class_list_v2()
    }

    // Retrieves a String list of all Classes in the given partition
    public String[] getStringClassList() {
        return getConnection().getLocalLBClass().get_string_class_list()
    }

    // Retrieves a Value list of all Classes in the given partition
    public String[] getValueClassList() {
        return getConnection().getLocalLBClass().get_value_class_list()
    }

    // Retrieves the specified Address Class in the given partition
    public LocalLBClassAddressClass[] getAddressClass(String[] names) {
        return getConnection().getLocalLBClass().get_address_class(names)
    }

    // Retrieves the specified External Class's file name in the given partition
    public String[] getExternalClassFileName(String[] names) {
        return getConnection().getLocalLBClass().get_external_class_file_name_v2(names)
    }

    // Retrieves the specified External Class's file format in the given partition
    public LocalLBClassFileFormatType[] getExternalClassFileFormat(String[] names) {
        return getConnection().getLocalLBClass().get_external_class_file_format(names)
    }

    // Retrieves the specified External Class's file mode in the given partition
    public LocalLBClassFileModeType[] getExternalClassFileMode(String[] names) {
        return getConnection().getLocalLBClass().get_external_class_file_mode(names)
    }

    // Retrieves the specified Address Class in the given partition
    public LocalLBClassStringClass[] getStringClass(String[] names) {
        return getConnection().getLocalLBClass().get_string_class(names)
    }

    // Retrieves the specified Address Class in the given partition
    public LocalLBClassValueClass getValueClass(String[] names) {
        return getConnection().getLocalLBClass().get_value_class(names)
    }


    // Retrieves all available information on given class
    public LocalLBClassMetaInformation[] getClassMetaInformation(String... names) {
        return getConnection().getLocalLBClass().get_class_meta_information_v2(names)
    }


    // Confirms if a Address Class exists
    // Will confirm with the partitions given up-to-date list
    public Boolean doesAddressClassExist(String name) {
        if (getAddressClassList().contains(name)) {
                return true
            }
        return false
    }

    // Confirms if a External Class exists
    // Will confirm with the partitions given up-to-date list
    public Boolean doesExternalClassExist(String name) {
        if (getExternalClassList().contains(name)) {
                return true
            }
        return false
    }

    // Confirms if a String Class exists
    // Will confirm with the partitions given up-to-date list
    public Boolean doesStringClassExist(String name) {
        if (getStringClassList().contains(name)) {
                return true
            }
        return false
    }

    // Confirms if a Value Class exists
    // Will confirm with the partitions given up-to-date list
    public Boolean doesValueClassExist(String name) {
        if (getValueClassList().contains(name)) {
                return true
            }
        return false
    }

    // Confirms that a class name does not exist in each of the four types
    // Note: No general method to check all classes at once
    public Boolean doesClassExist(String name) {
        if (doesAddressClassExist(name)) {
            return true
        }

        if (doesExternalClassExist(name)) {
            return true
        }

        if (doesStringClassExist(name)) {
            return true
        }

        if (doesValueClassExist(name)) {
            return true
        }
        return false
    }

    // Create an Address Data Group from a name and address entry (ip address and netmask mapping)
    public int createAddressClass(String name, LocalLBClassAddressEntry[] addressEntry) {
        def exitVal = 1
        if (!doesClassExist(name)) {
            println "Creating ${name} Address Data Group..."
            def adrClass = (new LocalLBClassAddressClass(name, addressEntry)) as LocalLBClassAddressClass[]
            def adrStub = getConnection().getLocalLBClass()
            adrStub.setTimeout(timeout * 1000)
            adrStub.create_address_class(adrClass)
            if (doesAddressClassExist(name)) {
                    println "Address Data Group successfully created!"
                    exitVal = 0
            }
            else {
                throw new Exception("Could not create Address Data Group!")
            }
        }
        else {
            println "Data Group ${name} already exists."
        }
        return exitVal
    }

    // Creates an AddressEntry object from a list of ip addresses and netmasks
    public LocalLBClassAddressEntry[] createAddressEntryArray(String[] ipAddresses, String[] netmasks) {
        if (ipAddresses.size() != netmasks.size()) {
            throw new Exception("The number of IP Addresses and Netmasks do not match. There must be the same number of IP Addresses and Netmasks. If necessary, use '255.255.255.255' as a default Netmask.")
        }
        def fullAdrEnt = []
        for (int i = 0; i < ipAddresses.size(); i++) {
            fullAdrEnt << new LocalLBClassAddressEntry(ipAddresses[i], netmasks[i])
        }
        return fullAdrEnt as LocalLBClassAddressEntry[]
    }

    // Assumes valid file_name, file_format, and file_mode are given
    public int createExternalClass(String name, String file_name, String file_format, String file_mode) {
        F5DataGroupFile dgf = new F5DataGroupFile(getConnection())
        if (!dgf.doesDataGroupFileExist(file_name)){
            throw new Exception("The Data Group File ${file_name} does not exist in this partition's " +
                "'Data Group File List'. Please add it before creating this External Data Group!")
        }

        def exitVal = 1
        if (!doesClassExist(name)) {
            println "Creating ${name} External Data Group..."
            def extStub = getConnection().getLocalLBClass()
            extStub.setTimeout(timeout * 1000)
            extStub.create_external_class_v2([name] as String[], [file_name] as String[])
            setExternalClassFileFormat(name, file_format)
            setExternalClassFileMode(name, file_mode)
            println "Set File Format: " + getExternalClassFileFormat([name] as String[])[0]
            println "Set File Mode: " + getExternalClassFileMode([name] as String[])[0]
            if (doesExternalClassExist(name)) {
                    println "External Data Group successfully created!"
                    exitVal = 0
            }
            else {
                throw new Exception("Could not create External Data Group!")
            }
        }
        else {
            println "Data Group ${name} already exists."
        }
        return exitVal
    }

    // Create a String Data Group a name and a list of members/record types (.jpg, .bmp. etc)
    public int createStringClass(String name, String[] members) {
        members = deNull(members)

        def exitVal = 1
        if (!doesClassExist(name)) {
            println "Creating ${name} String Data Group..."
            def strClass = (new LocalLBClassStringClass(name, members)) as LocalLBClassStringClass[]
            def strStub = getConnection().getLocalLBClass()
            strStub.setTimeout(timeout * 1000)
            strStub.create_string_class(strClass)
            if (doesStringClassExist(name)) {
                println "String Data Group successfully created!"
                exitVal = 0
            }
            else {
                throw new Exception("Could not create String Data Group!")
            }
        }
        else {
            println "Data Group ${name} already exists."
        }
        return exitVal
    }

    // Create a Value Data Group a name and a list of members/record types (positive integers)
    public int createValueClass(String name, Long[] members) {
        members = deNull(members)

        def exitVal = 1
        if (!doesClassExist(name)) {
            println "Creating ${name} Value Data Group..."
            def valClass = (new LocalLBClassValueClass(name, members)) as LocalLBClassValueClass[]
            def valStub = getConnection().getLocalLBClass()
            valStub.setTimeout(timeout * 1000)
            valStub.create_value_class(valClass)
            if (doesValueClassExist(name)) {
                    println "Value Data Group successfully created!"
                    exitVal = 0
            }
            else {
                throw new Exception("Could not create Value Data Group!")
            }
        }
        else {
            println "Data Group ${name} already exists."
        }
        return exitVal
    }

    // Delete any type of Class or Data Group
    public int deleteClass(String name){
        def exitVal = 1
        if (doesClassExist(name)) {
            println "Deleting ${name} Data Group..."
            def valStub = getConnection().getLocalLBClass()
            valStub.setTimeout(timeout * 1000)
            valStub.delete_class(name)
            if (!doesClassExist(name)) {
                    println "Data Group successfully deleted!"
                    exitVal = 0
            }
            else {
                throw new Exception("Could not delete Data Group!")
            }
        }
        else {
            println "Data Group ${name} does not exist."
        }
        return exitVal
    }

    // Modify the IP Addresses and Netmasks in specified address class
    public int modifyAddressClass(String name, LocalLBClassAddressEntry[] addressEntry) {
        def exitVal = 1
        if (doesAddressClassExist(name)) {
            println "Updating ${name} Address Data Group..."
            def adrClass = (new LocalLBClassAddressClass(name, addressEntry)) as LocalLBClassAddressClass[]
            def adrStub = getConnection().getLocalLBClass()
            adrStub.setTimeout(timeout * 1000)
            adrStub.modify_address_class(adrClass)
            println "Address Data Group successfully updated!"
            exitVal = 0
        }
        else {
            println "An Address Data Group ${name} does not exist."
        }
        return exitVal
    }

    // Modify the File Format and Mode of an external class
    public int modifyExternalClass(String name, String file_format, String file_mode) {
        def exitVal = 1
        if (doesExternalClassExist(name)) {
            println "Updating ${name} External Data Group..."
            setExternalClassFileFormat(name, file_format)
            setExternalClassFileMode(name, file_mode)
            println "External Data Group successfully updated!"
            exitVal = 0
        }
        else {
            println "External Data Group ${name} does not exist."
        }
        return exitVal
    }

    // Modify the String members of a string class
    public int modifyStringClass(String name, String[] members) {
        def exitVal = 1
        if (doesStringClassExist(name)) {
            println "Updating ${name} String Data Group..."
            def strClass = (new LocalLBClassStringClass(name, members)) as LocalLBClassStringClass[]
            def strStub = getConnection().getLocalLBClass()
            strStub.setTimeout(timeout * 1000)
            strStub.modify_string_class(strClass)
            println "String Data Group successfully updated!"
            exitVal = 0
        }
        else {
            println "A String Data Group ${name} does not exist."
        }
        return exitVal
    }

    // Modify the value members of a value class
    public int modifyValueClass(String name, Long[] members) {
        def exitVal = 1
        if (doesValueClassExist(name)) {
            println "Updating ${name} Value Data Group..."
            def valClass = (new LocalLBClassValueClass(name, members)) as LocalLBClassValueClass[]
            def valStub = getConnection().getLocalLBClass()
            valStub.setTimeout(timeout * 1000)
            valStub.modify_value_class(valClass)
            println "Value Data Group successfully updated!"
            exitVal = 0
        }
        else {
            println "A Value Data Group ${name} does not exist."
        }
        return exitVal
    }

    // Assumes name and file_format are valid
    public void setExternalClassFileFormat(String name, String file_format) {
        if (file_format != 'DEFAULT') {
            getConnection().getLocalLBClass()
                .set_external_class_file_format([name] as String[], (new LocalLBClassFileFormatType(file_format)) as LocalLBClassFileFormatType[])
        }
        else {
            println "File Format left unchanged..."
        }
    }

    // Assumes name and file_mode are valid
    // Possible bug: FILE_MODE_UNKNOWN is invalid file_mode input
    public void setExternalClassFileMode(String name, String file_mode) {
        if (file_mode != 'DEFAULT') {
            getConnection().getLocalLBClass()
                .set_external_class_file_mode([name] as String[], (new LocalLBClassFileModeType(file_mode)) as LocalLBClassFileModeType[])
        }
        else {
            println "File Mode Type left unchanged..."
        }
    }

    /** Supposedly valid method crashes. Possible iControl bug.
     * Unable to create an external data group that does not have a file object associated with it
     * Exception: Common::OperationFailed
     * primary_error_code   : 17241786 (0x010716BA)
     * secondary_error_code : 0
     * error_string         : 010716ba:3: External datagroup file (/Common/testt): Changing the datagroup file object is not allowed.
     */
    public void setExternalClassFileName(String name, String file_name) {
        println "Setting External Class's File Name to ${file_name}..."
        getConnection().getLocalLBClass()
            .set_external_class_file_name_v2([name] as String[], [file_name] as String[])
    }

    // Data Group attributes cannot be null, must be at least empty
    public String[] deNull(String[] r) {
        if (!r) {
            r = [""] as String[]
        }
        return r
    }
    // Data Group attributes cannot be null, must be at least empty
    public Long[] deNull(Long[] r) {
        if (!r) {
            r = new Long[1]
        }
        return r
    }
}
