func CoinFlip() bool {
	return rand.Intn(2) == 0
}
CoinFlip implements a coin flip witch returns true/false
package sgn
import (
	"encoding/binary"
	"errors"
	"fmt"
	"math/bits"
	"math/rand"
	"strings"
	"github.com/olekukonko/tablewriter"
)
var OPERANDS = []string{"XOR", "SUB", "ADD", "ROL", "ROR", "NOT"}
type SCHEMA []struct {
	OP  string
	Key []byte
}
type Encoder struct {
	architecture     int
	ObfuscationLimit int
	PlainDecoder     bool
	Seed             byte
	EncodingCount    int
	SaveRegisters    bool
}
func NewEncoder() *Encoder {
	var encoder Encoder
	encoder.architecture = 32
	encoder.ObfuscationLimit = 50
	encoder.PlainDecoder = false
	encoder.Seed = GetRandomByte()
	encoder.EncodingCount = 1
	encoder.SaveRegisters = false
	return &encoder
}
func (encoder *Encoder) SetArchitecture(arch int) error {
	switch arch {
	case 32:
		encoder.architecture = 32
	case 64:
		encoder.architecture = 64
	default:
		return errors.New("invalid architecture")
	}
	return nil
}
func (encoder *Encoder) GetArchitecture() int {
	return encoder.architecture
}
func (encoder *Encoder) Encode(payload []byte) ([]byte, error) {
	var final []byte
	if encoder.SaveRegisters {
		payload = append(payload, SafeRegisterSuffix[encoder.architecture]...)
	}
	garbage, err := encoder.GenerateGarbageInstructions()
	if err != nil {
		return nil, err
	}
	payload = append(garbage, payload...)
	cipheredPayload := CipherADFL(payload, encoder.Seed)
	encodedPayload, err := encoder.AddADFLDecoder(cipheredPayload)
	if err != nil {
		return nil, err
	}
	if encoder.PlainDecoder {
		final = encodedPayload
	} else {
		garbage, err = encoder.GenerateGarbageInstructions()
		if err != nil {
			return nil, err
		}
		encodedPayload = append(garbage, encodedPayload...)
		schemaSize := ((len(encodedPayload) - len(cipheredPayload)) / (encoder.architecture / 8)) + 1
		randomSchema := encoder.NewCipherSchema(schemaSize)
		obfuscatedEncodedPayload := encoder.SchemaCipher(encodedPayload, 0, randomSchema)
		final, err = encoder.AddSchemaDecoder(obfuscatedEncodedPayload, randomSchema)
		if err != nil {
			return nil, err
		}
	}
	if encoder.EncodingCount > 1 {
		encoder.EncodingCount--
		encoder.Seed = GetRandomByte()
		final, err = encoder.Encode(final)
		if err != nil {
			return nil, err
		}
	}
	if encoder.SaveRegisters {
		final = append(SafeRegisterPrefix[encoder.architecture], final...)
	}
	return final, nil
}
func CipherADFL(data []byte, seed byte) []byte {
	for i := 1; i < len(data)+1; i++ {
		current := data[len(data)-i]
		data[len(data)-i] ^= seed
		seed = byte((int(current) + int(seed)) % 256)
	}
	return data
}
func (encoder *Encoder) SchemaCipher(data []byte, index int, schema SCHEMA) []byte {
	for _, cursor := range schema {
		switch cursor.OP {
		case "XOR":
			binary.BigEndian.PutUint32(data[index:index+4], (binary.BigEndian.Uint32(data[index:index+4]) ^ binary.LittleEndian.Uint32(cursor.Key)))
		case "ADD":
			binary.LittleEndian.PutUint32(data[index:index+4], (binary.LittleEndian.Uint32(data[index:index+4])-binary.BigEndian.Uint32(cursor.Key))%0xFFFFFFFF)
		case "SUB":
			binary.LittleEndian.PutUint32(data[index:index+4], (binary.LittleEndian.Uint32(data[index:index+4])+binary.BigEndian.Uint32(cursor.Key))%0xFFFFFFFF)
		case "ROL":
			binary.LittleEndian.PutUint32(data[index:index+4], bits.RotateLeft32(binary.LittleEndian.Uint32(data[index:index+4]), -int(binary.BigEndian.Uint32(cursor.Key))))
		case "ROR":
			binary.LittleEndian.PutUint32(data[index:index+4], bits.RotateLeft32(binary.LittleEndian.Uint32(data[index:index+4]), int(binary.BigEndian.Uint32(cursor.Key))))
		case "NOT":
			binary.BigEndian.PutUint32(data[index:index+4], (^binary.BigEndian.Uint32(data[index : index+4])))
		}
		index += 4
	}
	return data
}
func RandomOperand() string {
	return OPERANDS[rand.Intn(len(OPERANDS))]
}
func GetRandomByte() byte {
	return byte(rand.Intn(255))
}
func GetRandomBytes(num int) []byte {
	slice := make([]byte, num)
	for i := range slice {
		slice[i] = GetRandomByte()
	}
	return slice
}