package asa

import (
	"encoding/json"
	"errors"
	"fmt"
	"regexp"
	"strings"

	"github.com/sirupsen/logrus"
	"github.rackspace.com/gorax/asaapi/pkg/asaModels"
	"github.rackspace.com/gorax/asaapi/pkg/logger"
	"github.rackspace.com/gorax/clients/sshAPI"
)

const timeout = 20

var (
	writeRE       = regexp.MustCompile(`(?i)(\[ok])`)
	pingSuccessRE = regexp.MustCompile(`(?i)success rate is 0 percent`)
	copyFileRE    = regexp.MustCompile(`(?i)([0-9]+)\s+bytes\s+copied\s+in\s+(\S+)\s+secs`)
	prompt        = `\S+#\s+$`
	configPrompt  = `\)#\s+$`
	bootVarRe     = regexp.MustCompile(`(?i)(\n|^)boot\s+variable\s+=\s+(\S+)\n`)
)

func niceOrRaw(err error) error {
	// Attempt to unmarshal for a cleaner error
	e := new(sshError)
	if decodeErr := json.Unmarshal([]byte(err.Error()), e); decodeErr == nil {
		return errors.New(e.Error)
	}
	logger.Log.WithError(err).Warn("error trying to unmarshal error from sshapi")
	return err
}

func (a *ASA) GetVersion() error {
	cmd, err := a.sshClient.PostReCommand("show version", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	a.Status.Version = asaModels.VersionParse(*cmd.Response)
	return nil
}

func (a *ASA) GetDHCPServer() error {
	cmd, err := a.sshClient.PostReCommand("show run dhcpd", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	return a.Config.DHCPServer.Parse(*cmd.Response)
}

func (a *ASA) GetPolicyMaps() error {
	cmd, err := a.sshClient.PostReCommand("show run policy-map", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	if err := a.Config.PolicyMaps.Parse(*cmd.Response); err != nil {
		return err
	}
	return nil
}

func (a *ASA) GetCheckSum() error {
	cmd, err := a.sshClient.PostReCommand("show checksum", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	if err := a.Status.CheckSum.Parse(*cmd.Response); err != nil {
		return err
	}
	return nil
}

func (a *ASA) GetCryptoKeys() error {
	cmd, err := a.sshClient.PostReCommand("show crypto key mypubkey rsa", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	if err := a.Config.Crypto.Keys.ParseRSA(*cmd.Response); err != nil {
		return err
	}

	cmd, err = a.sshClient.PostReCommand("show crypto key mypubkey ecdsa", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	if err := a.Config.Crypto.Keys.ParseECDSA(*cmd.Response); err != nil {
		return err
	}
	return nil
}

func (a *ASA) GetSNMP() error {
	cmd, err := a.sshClient.PostReCommand("show run all snmp-server", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	if err := a.Config.SNMP.Parse(*cmd.Response); err != nil {
		return err
	}
	return nil
}

func (a *ASA) GetClock() error {
	cmd, err := a.sshClient.PostReCommand("show run clock", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	a.Config.Clock.Parse(*cmd.Response)
	return a.GetNTP()
}

func (a *ASA) GetNTP() error {
	cmd, err := a.sshClient.PostReCommand("show run ntp", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	a.Config.Clock.NTP.Parse(*cmd.Response)
	return nil
}

func (a *ASA) GetHTTP() error {
	cmd, err := a.sshClient.PostReCommand("show run http", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	if err := a.Config.HTTP.Parse(*cmd.Response); err != nil {
		return err
	}
	return nil
}

func (a *ASA) GetServicePolicy() error {
	cmd, err := a.sshClient.PostReCommand("show run service-policy", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	if err := a.Config.ServicePolicies.Parse(*cmd.Response); err != nil {
		return err
	}
	return nil
}

func (a *ASA) GetFailover() error {
	if err := a.GetContext(); err != nil {
		return err
	}
	command := "show failover"
	cmd, cmdErr := a.sshClient.PostReCommand(command, prompt, timeout)
	if cmdErr != nil {
		return niceOrRaw(cmdErr)
	}
	a.AppendCommand(cmd)
	if strings.Contains(a.Info.PlatformModel, "Cisco ASA 5505") {
		var fo asaModels.FailoverStatus
		fo.Parse(*cmd.Response, "single")
		a.Status.Failover = &fo
		return nil
	}
	var fo asaModels.FailoverStatus
	fo.Parse(*cmd.Response, a.Status.Context.Mode)
	a.Status.Failover = &fo
	return nil
}

func (a *ASA) GetConfigFailover() error {
	cmd, err := a.sshClient.PostReCommand("show run failover", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var f asaModels.Failoverconfig
	f.Parse(*cmd.Response)
	a.Config.Failover = &f
	return nil
}

func (a *ASA) GetHostname() error {
	cmd, err := a.sshClient.PostCommand("show hostname", "# ", timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	// "response": "show hostname\ntFW1-asa5510\ntFW1-asa5510/pri/act# ",
	resp := strings.Split(*cmd.Response, "\n")
	a.Config.Hostname = resp[1]
	return nil
}

func (a *ASA) GetBanner() error {
	var blank []string
	a.Config.Banner = blank
	cmd, err := a.sshClient.PostCommand("show run banner", "# ", timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	banner := regexp.MustCompile(`banner motd (.*)\n`).FindAllStringSubmatch(*cmd.Response, -1)
	if len(banner) > 0 {
		for _, line := range banner {
			a.Config.Banner = append(a.Config.Banner, line[1])
		}
	}
	if a.Config.Banner == nil {
		a.Config.Banner = []string{}
	}
	return nil
}

func (a *ASA) GetNames() error {
	cmd, err := a.sshClient.PostReCommand("show run | in names|^name", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	a.Config.Names.Parse(*cmd.Response)
	return nil
}

func (a *ASA) GetAAAServers() error {
	cmd, err := a.sshClient.PostReCommand("show run aaa-server", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var aaa asaModels.AAAGroups
	aaa.Parse(*cmd.Response)
	a.Config.AAAServers = &aaa

	sshCMD, err := a.sshClient.PostReCommand("more system:running", prompt, 60)
	if err != nil {
		return niceOrRaw(err)
	}
	if err := aaa.ParseACSKeys(*sshCMD.Response); err != nil {
		return err
	}

	return nil
}

func (a *ASA) GetAAAConfig() error {
	cmd, err := a.sshClient.PostReCommand("show run aaa", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var ac asaModels.AAAConfig
	ac.Parse(*cmd.Response)
	a.Config.AAAConfig = &ac
	return nil
}

func (a *ASA) GetObjectGroups() error {
	cmd, err := a.sshClient.PostReCommand("show run object-group", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	// a.Config.ObjectGroups.Parse(*cmd.Response)
	a.AppendCommand(cmd)
	og := asaModels.ParseObjectGroups(*cmd.Response)
	a.Config.ObjectGroups = *og
	return nil
}

func (a *ASA) GetObjects() error {
	cmd, err := a.sshClient.PostReCommand("show run object", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	a.Config.Objects.Parse(*cmd.Response)
	return nil
}

func (a *ASA) GetPasswordPolicy() error {
	cmd, err := a.sshClient.PostReCommand("show run password-policy", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	return a.Config.PasswordPolicy.Parse(*cmd.Response)
}

func (a *ASA) GetInventory() error {
	cmd, err := a.sshClient.PostReCommand("show inventory", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	if err := a.Status.Inventory.Parse(*cmd.Response); err != nil {
		return err
	}
	return nil
}

// GetStartupErrors method will take an ASA type and execute/parse the
// show startup-config errors data on the target device.
func (a *ASA) GetStartupErrors() error {
	cmd, err := a.sshClient.PostReCommand("show startup-config errors", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	if err := a.Status.Startup.Parse(*cmd.Response); err != nil {
		return err
	}
	return nil
}

func (a *ASA) GetAccessGroups() error {
	cmd, err := a.sshClient.PostReCommand("show run access-group", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	ag := asaModels.ParseAccessGroups(*cmd.Response)
	a.Config.AccessGroups = ag.AccessGroups
	return nil
}

func (a *ASA) GetAccessLists() error {
	cmd, err := a.sshClient.PostReCommand("show run access-list", prompt, 120)
	if err != nil {
		return niceOrRaw(err)
	}
	logger.Log.WithFields(logrus.Fields{
		"Response": cmd,
	}).Debug()
	a.AppendCommand(cmd)
	var acl asaModels.AccessLists
	acl.Parse(*cmd.Response)
	a.Config.AccessLists = acl.AccessLists
	return nil
}

func (a *ASA) GetTelnet() error {
	cmd, err := a.sshClient.PostReCommand("show run telnet", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var t asaModels.Telnet
	t.Parse(*cmd.Response)
	a.Config.Telnet = t
	return nil
}

func (a *ASA) GetAccessList(ac string) error {
	cmd, err := a.sshClient.PostReCommand(fmt.Sprintf("show run access-list %s", ac), prompt, 120)
	if err != nil {
		return niceOrRaw(err)
	}
	logger.Log.WithFields(logrus.Fields{
		"Response": cmd,
	}).Debug()
	a.AppendCommand(cmd)
	var acl asaModels.AccessLists
	acl.Parse(*cmd.Response)
	a.Config.AccessLists = acl.AccessLists
	return nil
}

func (a *ASA) GetUsernames() error {
	cmd, err := a.sshClient.PostReCommand("show run username", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var u asaModels.UserNames
	u.Parse(*cmd.Response)
	a.Config.UserNames = u.UserNames
	return nil
}

func (a *ASA) GetCrypto() error {
	cmd, err := a.sshClient.PostReCommand("show run crypto", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	a.Config.Crypto.Parse(*cmd.Response)
	return nil
}

func (a *ASA) GetPrompt() error {
	cmd, err := a.sshClient.PostReCommand("show run prompt", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	m := regexp.MustCompile(`(?i)prompt (\S+)`).FindStringSubmatch(*cmd.Response)
	if len(m) != 2 {
		return fmt.Errorf("Error parsing prompt. Raw input: %v", *cmd.Response)
	}
	a.Config.Prompt = m[1]
	return nil
}

func (a *ASA) GetDHCPD() error {
	cmd, err := a.sshClient.PostReCommand("show run dhcpd", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	return a.Config.DHCPServer.Parse(*cmd.Response)
}

func (a *ASA) GetDomainName() error {
	cmd, err := a.sshClient.PostReCommand("show run domain-name", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	m := regexp.MustCompile(`\n(.*)\n`).FindStringSubmatch(*cmd.Response)
	if len(m) != 2 {
		return fmt.Errorf("Error parsing domain-name. Raw input: %v", *cmd.Response)
	}
	if strings.Contains(m[1], "domain-name") {
		a.Config.DomainName = m[1][len("domain-name "):] // Strip off domain-name from the beginning
	} else {
		a.Config.DomainName = m[1]
	}
	return nil
}

func (a *ASA) GetEnablePassword() error {
	cmd, err := a.sshClient.PostReCommand("show run enable", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	m := regexp.MustCompile(`\n(.*)\n`).FindStringSubmatch(*cmd.Response)
	if len(m) != 2 {
		return fmt.Errorf("Error parsing domain-name. Raw input: %v", *cmd.Response)
	}
	a.Config.EnablePassword = m[1]
	return nil
}

func (a *ASA) GetIPReversePath() error {
	cmd, err := a.sshClient.PostReCommand("show run ip verify reverse-path", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	m := regexp.MustCompile(`ip verify reverse-path interface (\S+)`).FindAllStringSubmatch(*cmd.Response, -1)
	for _, l := range m {
		a.Config.IPReversePathInts = append(a.Config.IPReversePathInts, l[1])
	}
	return nil
}

func (a *ASA) GetSSLConfig() error {
	cmd, err := a.sshClient.PostReCommand("show run ssl", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var sc asaModels.SSLConfig
	sc.Parse(*cmd.Response)
	a.Config.SSLConfig = &sc
	return nil
}

func (a *ASA) GetCertPkcs12() error {
	if a.Config.SSLConfig == nil {
		if err := a.GetSSLConfig(); err != nil {
			return err
		}
	}
	if len(a.Config.SSLConfig.Trustpoints) == 0 {
		return nil
	}

	var cmd []*sshAPI.Command
	cmd = append(cmd, sshAPI.ReCommandCreate("conf t", 5, configPrompt))
	for _, tp := range a.Config.SSLConfig.Trustpoints {
		cmd = append(cmd, sshAPI.ReCommandCreate(fmt.Sprintf("crypto ca export %s pkcs12 rack", *tp.Name), 20, configPrompt))
	}
	cmd = append(cmd, sshAPI.ReCommandCreate("end", 5, prompt))
	res, err := a.sshClient.PostCommands(cmd)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommands(res)
	for _, tp := range a.Config.SSLConfig.Trustpoints {
		for _, cmd := range res.Commands {
			if strings.Contains(cmd.Command, *tp.Name) {
				tp.PKCS12 = cmd.Response
			}
		}
	}
	return nil
}

func (a *ASA) GetContext() error {
	if strings.Contains(a.Info.PlatformModel, "Cisco ASA 5505") {
		a.Status.Context = asaModels.ContextInfo{Mode: "unsupported"}
		return nil
	}
	cmd, err := a.sshClient.PostReCommand("show mode", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	a.Status.Context.Parse(*cmd.Response)
	return nil
}

func (a *ASA) GetSSHConfig() error {
	cmd, err := a.sshClient.PostReCommand("show run ssh", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	a.Config.SSHConfig.Parse(*cmd.Response)
	return nil
}

func (a *ASA) GetArp() error {
	cmd, err := a.sshClient.PostReCommand("show arp", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var arp asaModels.ARPCache
	arp.Parse(*cmd.Response)
	a.Status.ARP = arp.ARPList
	return nil
}

func (a *ASA) GetRoutes() error {
	cmd, err := a.sshClient.PostReCommand("show route", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	rt := asaModels.ParseRouteTable(*cmd.Response)
	a.Status.Routes = rt.Routes
	return nil
}

func (a *ASA) GetInterfaces() error {
	cmd, err := a.sshClient.PostReCommand("show run interface", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var ic asaModels.InterfacesConfig
	ic.Parse(*cmd.Response)
	a.Config.Interfaces = ic.Interfaces
	return nil
}

func (a *ASA) ShowInterfaces() error {
	cmd, err := a.sshClient.PostReCommand("show interface", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	is := asaModels.ParseShowInterfaces(*cmd.Response)
	a.Status.Interfaces = is.Interfaces
	return nil
}

func (a *ASA) GetTunnelGroups() error {
	cmd, err := a.sshClient.PostReCommand("show run tunnel-group", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var tg asaModels.TunnelGroups
	tg.Parse(*cmd.Response)
	a.Config.TunnelGroups = tg.TunnelGroups
	return nil
}

func (a *ASA) GetNat() error {
	// If version isn't populated, populate it
	if a.Status.Version == nil {
		if err := a.GetVersion(); err != nil {
			return err
		}
	}

	var cmdToSend string
	switch {
	case a.Status.Version.SoftwareVer.Major <= 8 && a.Status.Version.SoftwareVer.Minor < 3:
		cmdToSend = "show run static"
	default:
		cmdToSend = "show run nat"
	}

	cmd, err := a.sshClient.PostReCommand(cmdToSend, prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}

	a.AppendCommand(cmd)
	a.Config.Nat = asaModels.ParseNat(*cmd.Response)
	return nil
}

// GetNatStatus method will take an initialized ASA as a receiver, and pull the nat
// details from the ASA. The response will be pared into the Status.Nat object.
func (a *ASA) GetNatStatus() error {
	// We may need to resolve names for some NAT addresses. We need
	// to pull a list of the names configuration if it hasn't already
	// been collected.
	if len(a.Config.Names.NameList) == 0 {
		if err := a.GetNames(); err != nil {
			return err
		}
	}
	cmd, err := a.sshClient.PostReCommand("show nat detail", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}

	a.AppendCommand(cmd)
	natStatus := new(asaModels.NatStatus)
	natStatus.Parse(*cmd.Response, a.Config.Names)
	a.Status.Nat = *natStatus
	return nil
}

func (a *ASA) GetStatics() error {
	cmd, err := a.sshClient.PostReCommand("show run static", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	s := asaModels.ParseStatics(*cmd.Response)
	a.Config.Statics = s.Statics
	return nil
}

func (a *ASA) GetConn(filter string) error {
	cmdString := "show conn"
	if filter != "" {
		cmdString = fmt.Sprintf("show conn | inc %s", filter)
	}
	cmd, err := a.sshClient.PostReCommand(cmdString, prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var conns asaModels.Conns
	conns.Parse(*cmd.Response)
	a.Status.Conns = conns.ConnTable
	return nil
}

// GetXlate method will execute the `show xlate` command on an ASA and populate the Status.Xlate
// complex type with the parsed results.
func (a *ASA) GetXlate() error {
	cmd, err := a.sshClient.PostReCommand("show xlate", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var xlate asaModels.Xlate
	xlate.Parse(*cmd.Response)
	a.Status.Xlate = xlate
	return nil
}

func (a *ASA) GetGroupPolicies() error {
	cmd, err := a.sshClient.PostReCommand("show run group-policy", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var gp asaModels.GroupPolicies
	gp.Parse(*cmd.Response)
	a.Config.GroupPolicies = gp.GroupPolicies
	return nil
}

func (a *ASA) VPNSessionL2L() ([]*asaModels.Lan2Lan, error) {
	cmd, err := a.sshClient.PostReCommand("show vpn-sessiondb l2l", prompt, 30)
	if err != nil {
		return nil, niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var l2l asaModels.Lan2Lans
	l2l.Parse(*cmd.Response)
	return l2l.Lan2Lans, nil
}

func (a *ASA) VPNSessionRemote(legacy bool) ([]*asaModels.RATunnel, error) {
	command := "show vpn-sessiondb ra-ikev1-ipsec"
	if legacy {
		command = "show vpn-sessiondb remote"
	}
	cmd, err := a.sshClient.PostReCommand(command, prompt, 30)
	if err != nil {
		return nil, niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var ra asaModels.RATunnels
	ra.Parse(*cmd.Response)
	return ra.RATunnels, nil
}

func (a *ASA) ShowAccessLists() error {
	cmd, err := a.sshClient.PostReCommand("show access-list", prompt, 120)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var sa asaModels.ShowAccessLists
	sa.Parse(*cmd.Response)
	a.Status.ACL = sa.AccessLists
	return nil
}

func (a *ASA) ShowAccessList(acl string) (*asaModels.ShowAccessList, error) {
	cmd, err := a.sshClient.PostReCommand("show access-list "+acl, prompt, 45)
	if err != nil {
		return nil, niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var sa asaModels.ShowAccessLists
	sa.Parse(*cmd.Response)
	return sa.FindACL(acl), nil
}

func (a *ASA) WriteMem() error {
	cmd, err := a.sshClient.PostReCommand("write mem", prompt, 25)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	if !writeRE.MatchString(*cmd.Response) {
		return fmt.Errorf("Unable to match write mem prompt: %s", *cmd.Response)
	}

	return nil
}

func (a *ASA) BackupStartup(destination string) error {
	cmd, err := a.sshClient.PostReCommand(fmt.Sprintf("copy /noconfirm startup-config disk0:/%s", destination), prompt, 90)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	if !copyFileRE.MatchString(*cmd.Response) {
		return fmt.Errorf("Unable to match copy success string, found: %s", *cmd.Response)
	}
	return nil

}

func (a *ASA) MoreSystem() (string, error) {
	cmd, err := a.sshClient.PostReCommand("more system:running-config", prompt, 120)
	if err != nil {
		return "", niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	return *cmd.Response, nil
}

func (a *ASA) ShowRun() (string, error) {
	cmd, err := a.sshClient.PostReCommand("show run", prompt, 1200)
	if err != nil {
		return "", niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	return *cmd.Response, nil
}

func (a *ASA) TFTPToFlash(source string, timeout int) (string, error) {
	var errorCheckRE = regexp.MustCompile(`(?i)%(.*?)\n+`)
	cmd, err := a.sshClient.PostReCommand(fmt.Sprintf("copy /noconfirm tftp://%s flash:", source), prompt, 480)
	if err != nil {
		return "", niceOrRaw(err)
	}
	a.AppendCommand(cmd)

	if strings.Contains(*cmd.Response, "Unspecified Error") == true {
		copyerr := errorCheckRE.FindStringSubmatch(*cmd.Response)
		return *cmd.Response, errors.New(copyerr[1])

	}
	// logger.Log.Println(*cmd.Response)
	return *cmd.Response, nil
}

func (a *ASA) HTTPToFlash(source string, timeout int) (string, error) {
	var errorCheckRE = regexp.MustCompile(`(?i)%(.*?)\n+`)
	cmd, err := a.sshClient.PostReCommand(fmt.Sprintf("copy /noconfirm http://%s flash:", source), prompt, timeout)
	if err != nil {
		return "", niceOrRaw(err)
	}
	a.AppendCommand(cmd)

	if err := asaRespHandler(*cmd.Response); err != nil {
		return "", err
	}

	if strings.Contains(*cmd.Response, "bytes copied in") != true {
		copyerr := errorCheckRE.FindStringSubmatch(*cmd.Response)
		return *cmd.Response, errors.New(copyerr[1])
	}
	return *cmd.Response, nil
}

func (a *ASA) DiskInfo(disk string) (*asaModels.Disk, error) {
	cmd, err := a.sshClient.PostReCommand(fmt.Sprintf("dir %s", disk), prompt, timeout)
	if err != nil {
		return nil, niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var d asaModels.Disk
	d.Parse(*cmd.Response)
	return &d, nil
}

func (a *ASA) GetLogging() error {
	cmd, err := a.sshClient.PostReCommand("sh run logging", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	var l asaModels.Logging
	if err := l.Parse(*cmd.Response); err != nil {
		return err
	}
	l.Populated = true
	a.Config.Logging = l
	return nil
}

func (a *ASA) ListDisk() error {
	//if a.Status.Disk != nil {
	//	return nil
	//} // commented out because it may be caching, this'll force a new dir to be run
	d, err := a.DiskInfo("disk0:")
	if err != nil {
		return err
	}
	a.Status.Disk = d
	return nil
}

func (a *ASA) DeleteFile(disk, filename string) (*string, error) {
	path := fmt.Sprintf("delete /noconfirm %s:/%s", disk, filename)
	cmd, err := a.sshClient.PostReCommand(path, prompt, timeout)
	if err != nil {
		return nil, niceOrRaw(err)
	}
	a.AppendCommand(cmd)
	return cmd.Response, nil
}

func (a *ASA) SetConfReg(confReg string) (string, error) {
	_, err := a.sshClient.PostReCommand("conf t", configPrompt, timeout)
	if err != nil {
		return "", niceOrRaw(err)
	}
	cmd, err := a.sshClient.PostReCommand("config-register "+confReg, configPrompt, timeout)
	if err != nil {
		return "", niceOrRaw(err)
	}
	_, err = a.sshClient.PostReCommand("end", prompt, timeout)
	if err != nil {
		return "", niceOrRaw(err)
	}
	return *cmd.Response, nil
}

func (a *ASA) BootList() error {
	cmd, err := a.sshClient.PostReCommand("show run boot", prompt, timeout)
	if err != nil {
		return niceOrRaw(err)
	}
	var bs asaModels.BootSystem
	bs.Parse(*cmd.Response)
	a.Config.BootList = bs.BootFiles
	return nil
}

func (a *ASA) BootVar() ([]string, error) {
	cmd, err := a.sshClient.PostReCommand("show bootvar", prompt, timeout)
	if err != nil {
		return nil, niceOrRaw(err)
	}
	bootvar := bootVarRe.FindStringSubmatch(*cmd.Response)
	if len(bootvar) == 0 {
		return nil, nil
	}
	return strings.Split(bootvar[2], ";"), nil
}

// GetBootVar method will parse and return a type of BootVar that contains the data from the
// 'show bootvar' command on a Cisco ASA
func (a *ASA) GetBootVar() (bv asaModels.BootVar, err error) {
	cmd, err := a.sshClient.PostReCommand("show bootvar", prompt, timeout)
	if err != nil {
		return bv, niceOrRaw(err)
	}
	bv = asaModels.ParseBootVar(*cmd.Response)
	return
}

func (a *ASA) GetProxyARP() error {
	cmd, err := a.sshClient.PostReCommand("show run all sysopt", prompt, 30)
	if err != nil {
		return niceOrRaw(err)
	}
	a.Config.ProxyARP = asaModels.ParseProxyARP(*cmd.Response)
	return nil
}

func (a *ASA) Ping(ip string) (bool, string, error) {
	ping := fmt.Sprintf("ping %s repeat 2", ip)
	cmd, err := a.sshClient.PostReCommand(ping, prompt, 30)
	if err != nil {
		return false, "", niceOrRaw(err)
	}

	if pingSuccessRE.MatchString(*cmd.Response) {
		return false, *cmd.Response, nil
	}

	return true, *cmd.Response, nil
}

func (a *ASA) RunCommands(cmd []*sshAPI.Command) (*sshAPI.SSHCommands, error) {
	res, err := a.sshClient.PostCommands(cmd)
	if err != nil {
		return nil, niceOrRaw(err)
	}
	return res, nil
}

func (a *ASA) RunCommand(command string) (*string, error) {
	cmd, err := a.sshClient.PostReCommand(command, prompt, timeout)
	if err != nil {
		return nil, niceOrRaw(err)
	}
	return cmd.Response, nil
}

func (a *ASA) GetWebVPN() error {
	res, err := a.RunCommand("show run webvpn")
	if err != nil {
		return niceOrRaw(err)
	}
	return a.Config.WebVPN.Parse(*res)
}
