//
//  EDENON.swift
//  JJUNGTABLE
//
//  Created by Sean Kim on 6/13/24.
//

import Foundation
import Combine

/*
암호화 (5단계로 구성)
예시)
   값: 12345678
   키: 20240612

0단계
암호화 시킬 번호와 키의 자리수가 둘이 같은지 확인

1단계
암호화 시킬 값과 키를 자리수 대로 덧셈을 해준다.
   - 만일 1자리 수인 경우에는 앞에 0을 붙여서 표시한다.
예시)
   1 2 3 4 5 6 7 8
   2 0 2 4 0 6 1 2

결과: 03 02 05 08 05 12 08 10

2단계
1단계의 결과(길이: n)에서 1번째와 n번째를 묶고 2번째와 n-1번째 3번째와 n-2번째를 중앙이 될 때까지 그룹을 만들어 준다.
예시)
   03 02 05 08 05 12 08 10

결과: 0310 0208 0512 0805

3단계
2단계의 결과에서 나타난 그룹들을 순서에 맞게 특정한 동작을 수행한다.
첫번째 그룹은 1번째 항목의 숫자를 떼서 남아있는 숫자의 뒤에 붙이고,
두번째 그룹은 1~2번째 항목의 숫자를 떼서 남아있는 숫자의 뒤에 붙이고,
세번째 그룹은 1~3번째 항목을 떼서 남아있는 숫자의 뒤에 붙인다.
네번째 그룹은 1~4번째 항목을 떼서 남아있는 숫자의 뒤에 붙인다.
예시)
   0310 0208 0512 0805
   첫번째: 0310 -> 0 을 뗀다 -> 남아있는 수: 310 -> 뒤에 붙인다 = 3100
   두번째: 0208 -> 02 를 뗀다 -> 남아있는 수: 08 -> 뒤에 붙인다 = 0802
   세번쨰: 0512 -> 051 을 뗀다 -> 남아있는 수: 2 -> 뒤에 붙인다 = 2051
   네번째: 0805 -> 0805 를 뗀다 -> 남아있는 수:  -> 뒤에 붙인다 = 0805

결과: 3100 0802 2051 0805

4단계
3단계의 결과에서 각 그룹의 숫자들을 위치를 맞춰 다시 그룹화를 진행한다.

예시)
   3100 0802 2051 0805
   [새 그룹]
   첫번째: 3 0 2 0
   두번째: 1 8 0 8
   세번째: 0 0 5 0
   네번째: 0 2 1 5

결과: 3020 1808 0050 0215
   
5단계
4단계의 결과에서 진행을 하는데 이해를 위해서 변수(isToggle: default = false)를 하나 만들어서 설명하겠다.
4단계의 결과에서 숫자를 영문자로 변환하는 작업을 진행한다.
1 = A, 2 = B, 3 = C, 4 = D, 5 = E, 6 = F, 7 = G, 8 = H, 9 = I 에 맞춰서 바꿔주는데 isToggle의 값이 false면 대문자로 true이면 소문자로 변환을 해준다.

하지만 0을 만나게 되면 isToggle의 Bool값을 바꿔주며 0으로 이루어진 그룹이 존재 할경우 1부터 9에 치환되는 알파벳이 아닌 다른 알파벳 (J~Z)를 대문자로 표시해준다.
 
 만약 0이 연속되는 숫자가 나오게 될경우 2자리 이상일 경우에는 0의 갯수를 표시해준다. 4자리인 경우에는 앞의 예외를 처리 한다.

예시)
   3020 1808 0050 0215
   숫자 - 출력값 - isToggle
   첫번째
   3 - C - F
   0 -   - T
   2 - b - T
   0 -   - F
   첫번째 그룹이 0000 이 아니므로 다음 작업 진행
   만약 0000일 경우에 (J~Z)중 랜덤하게 아무 값을 넣는다

   두번쨰
   1 - A - F
   8 - H - F
   0 -   - T
   8 - h - T
   두번째 그룹이 0000 이 아니므로 다음 작업 진행
   만약 0000일 경우에 (J~Z)중 랜덤하게 아무 값을 넣는다

   세번째
   0 -   - F
   0 - 2 - T - 이때 0이 2번 반복 되었으므로 2를 표시
   5 - e - T
   0 -   - F
   세번째 그룹이 0000 이 아니므로 다음 작업 진행
   만약 0000일 경우에 (J~Z)중 랜덤하게 아무 값을 넣는다

   네번째
   0 - 2 - T - 세번째 그룹의 마지막에서도 0이 나와서 이번까지 0이 2번 반복되었기에 2를 표시
   2 - b - T
   1 - a - T
   5 - e - T
   네번째 그룹이 0000 이 아니므로 다음 작업 진행
   만약 0000일 경우에 (J~Z)중 랜덤하게 아무 값을 넣는다

결과: Cb AHh 2e 2bae


이렇게 진행한다.
값: 12345678
키: 20240612
결과: Cb AHh 2e 2bae

 */

struct ENDC_ERROR:Error {
    var caseType: CASE
    var message: String
    
    enum CASE: Error{
        case EN_DIGIT
        case DC_WRONG
    }
}

func edon_E(value: String, key: String) -> Future<String, ENDC_ERROR> {
    return Future { promise in
        var result = ""
        
        // 0단계: 자리수 확인
        if value.count != key.count { promise(.failure(ENDC_ERROR(caseType: .EN_DIGIT, message: "digit ERROR"))) }

        /// 1단계: 자리별 덧셈 + 그룹화 + 셔플
        /// <순서>
        /// 1. 암호화 할 값과 키를 자리 별로 덧셈을 진행
        /// 2. 1의 결과가 1자리일 경우에는 앞에 0을 붙여서 2자리로 만들어줌
        /// 3. 0 ~ index 까지의 배열을 [index+1... end] 뒤에 붙여준다.
        /// 4. 3의 결과로 만들어진 그룹에서 각 자리 위치에 있는 숫자들로 새로운 그룹을 만들어준다.
        /// 5. 4의 결과를 가지고 하나의 String 값으로 만들어 반환한다.
        let sum = { () -> String in
            var tempArray = ["","","",""]
            let add = zip(value, key).map{
                guard let first = Int(String($0)), let second = Int(String($1)) else { return ""}
                return String(format: "%02d", first + second)
            }
            
            zip(add[...3],add[4...].reversed())
                .map{"\($0)\($1)"}.enumerated()
                .map { (index, value) in
                    let sumValue = value.map{$0}
                    return "\(String(sumValue[(index+1)...]))\(String(sumValue[...(index)]))"
                }
                .joined(separator: "")
                .enumerated().map { (index, value) in
                    tempArray[index % 4] += "\(value)"
                }
//            print(tempArray)
//            print(tempArray.joined(separator: ""))
            return tempArray.joined(separator: "")
        }()
        
//        print(sum)
        
//        print(String(Character(UnicodeScalar(73))))
        //"I".unicodeScalars.first?.value
        
        /// 2단계
        /// [영문자 아스키 코드 : 65~73]
        /// <순서>
        /// 1.
        var isToggle = false
        var zCnt = 0
        
        let ascii = { (value: Int) -> String in
            if let scalar = UnicodeScalar(value) { return "\(scalar)" }
            return ""
        }

        for value in sum {
            if value == "0" {
                isToggle.toggle()
                zCnt += 1
                if zCnt == 4 {
                    result += isToggle ? ascii(Int.random(in: 74...90)).lowercased() : ascii(Int.random(in: 74...90))
                    zCnt = 0
                }
            }
            else {
                if zCnt > 1 { result += "\(zCnt)" }
                zCnt = 0
                result += isToggle ? ascii(Int("\(value)")!+64).lowercased() : ascii(Int("\(value)")!+64)
            }
        }
        promise(.success(result))
    }
}
func edon_D(value: String, key: String) -> Future<String, ENDC_ERROR> {
    return Future { promise in
        var result = ""
        var temp = ""
        var isToggle = false
        
        let ascii = { (value: String) -> Int in
            if let scalar = value.unicodeScalars.first, let sInt = Int("\(scalar.value)") { return sInt }
            return 0
        }
        
        // 대 = 65~73, 소 = 97~105
        value.map {
            if let codeNum = Int("\($0)") {
                for _ in 0..<codeNum {
                    temp += "0"
                    isToggle.toggle()
                }
            }
            else {
                let code = ascii("\($0)")
                
                if code > 64 && code < 74 {
                    if isToggle {
                        temp += "0\(code-64)"
                        isToggle.toggle()
                    }
                    else { temp += "\(code-64)" }
                }
                else if code > 96 && code < 106 {
                    
                    if !isToggle {
                        temp += "0\(code-96)"
                        isToggle.toggle()
                    }
                    else { temp += "\(code-96)" }
                }
                else {
                    temp += "0000"
                }
            }
        }
        
        if temp.count % 4 > 0 { for _ in 0 ..< 4-(temp.count%4) { temp += "0" } }
        
        var tempArray = ["","","",""]
        temp.enumerated().map {(index,value) in tempArray[index%4] += "\(value)" }
        
        var saveArray = [String].init(repeating: "", count: 8)
        var saveTArray = [[String]]()
        
        
        tempArray.enumerated().map {(index,value) in
            let sepValue = value.map{$0}
            
            saveTArray.append("\(String(sepValue[(3-index)...]))\(String(sepValue[...(3-index-1)]))".map{"\($0)"})

            saveArray[index] = saveTArray[index][0...1].joined(separator: "")
            saveArray[(7-index)] = saveTArray[index][2...3].joined(separator: "")
        }
        
        for (v,k) in zip(saveArray,key) {
            if let value = Int(v), let key = Int("\(k)") {
                if value-key > 0 { result += "\(value-key)" }
                else { result += "\((value-key) * -1)" }
            }
        }
        
        promise(.success(result))
    }
}
