//
//  InTextLib.swift
//  forShip
//
//  Created by Mamoru Sugita on 2023/10/16.
//

import SwiftUI
import Combine


///入力文字数制限用
enum TextLengthType : UInt16 {
    case FreeLength     // 制約がない
    case LessEqual      // 以下
    case OnlyEqual      // 等しい
    case GreaterEqual   // 以上
    case Range          // 範囲:xx以上、yy以下
    case MultipleEqual  // 複数文字数のor指定。「6文字か9文字」とか
}
/// 文字数制限構造体
/// 範囲指定の場合は、以上と以下で挟む
struct LengthConstraint {
    static let Maxlength : UInt16 = 256  // 仕様上の最大文字数
    var lcType : TextLengthType = .FreeLength
    var lcEnableEmpty : Bool = true     //  空文字列("") を有効とするかどうか。既存文字列の削除に使う場合がある
    var lcSize : [UInt16] = []          // 通常は1要素。範囲指定:2要素、or指定:2要素以上
}

/// 文字セット
enum CharKind : UInt16 {
    case FreeCharset    // 改行コードを除くUnicode文字全て
    case Numbers        // 10進アラビア数字
    case Alphanumeric   // アルファベットと10進数
    case UAlphanumeric  // 大文字アルファベットと10進数
    case LAlphanumeric  // 小文字アルファベットと10進数
}
/// テキスト制約構造体
struct TextConstraint {
    var charSet : CharKind = .FreeCharset
    var textStructure : String = "" // テキストの詳しい構造を正規表現で指定　Alphanumeric, UAlphanumeric, NamesCharの場合のみ有効
    var isPasswordtext : Bool = false   // パスワードかどうか?:表示の他、文字種類に特別な制約がかかる可能性あり
    var length : LengthConstraint =  LengthConstraint()
}

///TextFiled入力制限（入力中）設定用
struct TextFieldModifier: ViewModifier{
    @Binding var text: String
    let inputType: TextInputType
    func body(content: Content) -> some View {
            content
                .onReceive(Just(text), perform: { _ in
                    text = InTextLib.checkCharSet(text, inputType.constraint)
                    text = InTextLib.checkTextLengthEditing(text, inputType.constraint.length)
                })
        }
    
    
}

///テキスト入力パターン
enum TextInputType{
    case Free           //制限なし
    case Name           //名前
    case UserId         //ユーザーID
    
    
    var constraint : TextConstraint{
        switch self {
        case .Free:
            var constraint = TextConstraint()
            constraint.charSet = .FreeCharset
            var lengthConst = LengthConstraint()
            lengthConst.lcType = .LessEqual
            lengthConst.lcEnableEmpty = true
            lengthConst.lcSize.append(LengthConstraint.Maxlength)
            constraint.length = lengthConst
            return constraint
        case .Name:
            var constraint = TextConstraint()
            constraint.charSet = .FreeCharset
            var lengthConst = LengthConstraint()
            lengthConst.lcType = .LessEqual
            lengthConst.lcEnableEmpty = true
            lengthConst.lcSize.append(20)
            constraint.length = lengthConst
            return constraint
        case .UserId:
            var constraint = TextConstraint()
            constraint.charSet = .FreeCharset
            constraint.textStructure = "^[!-~]{30}$"
            var lengthConst = LengthConstraint()
            lengthConst.lcType = .OnlyEqual
            lengthConst.lcEnableEmpty = false
            lengthConst.lcSize.append(30)
            constraint.length = lengthConst
            return constraint
        }
    }
}
/// テキスト入力支援クラス
class InTextLib {
    /// 文字種類をチェック
    static func checkCharSet( _ text: String, _ constraint : TextConstraint) -> String {
        var retText = text
        switch constraint.charSet {
        case .FreeCharset :   // 絵文字を除くUnicode文字全て
             retText = text
        case .Numbers :       // 10進アラビア数字
            retText = text.filter { char in
                char.isNumber
            }
        case .Alphanumeric :  // アルファベットと10進数
            let validCodes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
            let sets = CharacterSet(charactersIn: validCodes)
            retText = String(text.unicodeScalars.filter(sets.contains).map(Character.init))

        case .UAlphanumeric : // 大文字アルファベットと10進数
            let validCodes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
            let sets = CharacterSet(charactersIn: validCodes)
            retText = String(text.unicodeScalars.filter(sets.contains).map(Character.init)).uppercased()
        case .LAlphanumeric : // 小文字アルファベットと10進数
            let validCodes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
            let sets = CharacterSet(charactersIn: validCodes)
            retText = String(text.unicodeScalars.filter(sets.contains).map(Character.init)).lowercased()
        }
        return retText
    }
    /// 文字数をチェック
    static func checkTextLengthEditing( _ text: String, _ conrec : LengthConstraint )-> String {
        var retText = text
        switch conrec.lcType {
        case .LessEqual, .OnlyEqual :
            if text.count > conrec.lcSize[0] {
                retText = String(text.suffix(Int(conrec.lcSize[0])))
            }
        case .MultipleEqual, .Range :
            if text.count > conrec.lcSize.max() ?? LengthConstraint.Maxlength {
                retText = String(text.suffix(Int(conrec.lcSize.max() ?? LengthConstraint.Maxlength)))
            }
        default:
            break
        }
        return retText
    }
    
    /// 文字数をチェック
    static func checkTextLengthEndEdit( _ text: String, _ conrec : LengthConstraint )-> Bool {
        switch conrec.lcType {
        case .GreaterEqual :
            if text.count < conrec.lcSize[0] {
                return false
            }
        case .LessEqual :
            if text.count > conrec.lcSize[0] {
                return false
            }
            if !conrec.lcEnableEmpty && text.count == 0 {
                return false
            }
        case .OnlyEqual :
            if text.count < conrec.lcSize[0] {
                return false
            }
        case .Range :
            let textmin : UInt16 = conrec.lcSize.min() ?? 1
            if text.count < textmin {
                return false
            }
            let textmax : UInt16 = conrec.lcSize.max() ?? LengthConstraint.Maxlength
            if text.count > textmax {
                return false
            }
        case .MultipleEqual :
            if conrec.lcSize.firstIndex(where: { $0 == UInt16(text.count) }) == nil {
                return false
            }
        default:
            break
        }
        return true
    }
}


extension StringProtocol where Self: RangeReplaceableCollection {
  var removeNewlines: Self {
    filter { !$0.isNewline }
  }
}
