//
//  CalculationDelegate.swift
//  Calculator
//
//  Created by Cal Stephens on 9/26/16.
//  Copyright © 2016 Cal Stephens. All rights reserved.
//

import Foundation


//MARK: - Protocols that can be implemented yourself if you'd like

protocol CalculationDelegate {
    
    var previousExpressions: [(expression: String, result: String)] { get set }
    var currentOperator: Operator? { get set }
    
    var leftNumber: Double? { get set }
    var rightNumber: Double? { get set }
    var resultNumber: Double { get }
    
    var expressionString: String { get }
    
    func handleInput(_ number: Int)
    func setOperator(_ newOperator: Operator)
    func clearInputAndSave(_ save: Bool)
    
}

protocol Operator {
    
    var character: String { get }
    var operate: (Double, Double) -> Double { get }
    init(forCharacter character: String, withFunction: @escaping (Double, Double) -> Double)
    
}


//MARK: - Default Implementation of the Calculation Protocol

class DefaultCalculationDelegate : CalculationDelegate {
    
    var previousExpressions: [(expression: String, result: String)] = []
    var currentOperator: Operator?
    
    var leftNumber: Double?
    var rightNumber: Double?
    var resultNumber: Double {
        if let leftNumber = leftNumber {
            return rightNumber ?? leftNumber
        }
        return leftNumber ?? Double(previousExpressions.last?.result ?? "a") ?? 0.0
    }
    
    var expressionString: String {
        if let left = leftNumber?.roundedString {
            if let operatorChar = currentOperator?.character {
                let right = rightNumber?.roundedString ?? "0"
                return "\(left) \(operatorChar) \(right) = "
            }
            return "\(left) = "
        }
        return "0 = "
    }
    
    func handleInput(_ input: Int) {
        
        //fixes equals bug where it would just append to the end of the previous answer
        if expressionString == "" {
            leftNumber = nil
        }
        
        //update the left number until the operator has been set
        let useLeft = (currentOperator == nil)
        
        //update numbers
        let optionalNumber = useLeft ? leftNumber : rightNumber
        var newNumber = optionalNumber ?? 0.0
        newNumber = (newNumber * 10) + Double(input)
        
        if useLeft { leftNumber = newNumber }
        else { rightNumber = newNumber }
    }
    
    func setOperator(_ newOperator: Operator) {
        if leftNumber == nil {
            leftNumber = Double(previousExpressions.last?.result ?? "a") ?? 0.0
        }
        
        //if there's a number on the Right,
        if rightNumber != nil {
            clearInputAndSave(true)
            leftNumber = resultNumber
        }
        
        currentOperator = newOperator
    }
    
    func clearInputAndSave(_ save: Bool) {
        
        if !save {
            previousExpressions.append((expression: "cleared", result: ""))
        }
        
        let result = currentOperator?.operate(leftNumber ?? 0.0, rightNumber ?? 0.0)
        
        if leftNumber != nil || rightNumber != nil || currentOperator != nil {
            if let result = result {
                previousExpressions.append((expression: expressionString, result: "\(result)"))
            }
        }
        
        leftNumber = nil
        rightNumber = nil
        currentOperator = nil
    }
    
}


//MARK: - Default Implementation of the Operator protocol

struct DefaultOperator : Operator {
    
    var character: String
    var operate: (Double, Double) -> Double
    
    init(forCharacter character: String, withFunction operate: @escaping (Double, Double) -> Double) {
        self.character = character
        self.operate = operate
    }
    
}


//MARK: - Double Extension, adds .roundedString variable

extension Double {
    
    var roundedString: String {
        let rounded = (self * 100).rounded() / 100
        let string = "\(rounded)"
        return string.hasSuffix(".0") ? "\(Int(self))" : string
    }
    
}