//
// Copyright 2019 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import UIKit

final class ImageEditorTextItem: ImageEditorItem, ImageEditorTransformable {

    let text: String

    let color: ColorPickerBarColor

    let decorationStyle: MediaTextView.DecorationStyle

    let textStyle: MediaTextView.TextStyle

    let fontSize: CGFloat
    static let defaultFontSize: CGFloat = 36
    var font: UIFont {
        MediaTextView.font(for: textStyle, withPointSize: fontSize)
    }

    var textForegroundColor: UIColor {
        switch decorationStyle {
        case .none, .whiteBackground: return color.color

        case .coloredBackground:
            let backgroundColor = color.color
            return backgroundColor.isCloseToColor(.white) ? .black : .white

        case .outline, .underline: return .white
        }
    }

    var textBackgroundColor: UIColor? {
        switch decorationStyle {
        case .none, .underline, .outline: return nil

        case .whiteBackground:
            let textColor = color.color
            return textColor.isCloseToColor(.white) ? .black : .white

        case .coloredBackground: return color.color
        }
    }

    var textDecorationColor: UIColor? {
        switch decorationStyle {
        case .none, .whiteBackground, .coloredBackground: return nil
        case .outline, .underline: return color.color
        }
    }

    // In order to render the text at a consistent size
    // in very differently sized contexts (canvas in
    // portrait, landscape, in the crop tool, before and
    // after cropping, while rendering output),
    // we need to scale the font size to reflect the
    // view width.
    //
    // We use the image's rendering width as the reference value,
    // since we want to be consistent with regard to the image's
    // content.
    let fontReferenceImageWidth: CGFloat

    let unitCenter: ImageEditorSample

    // Leave some margins against the edge of the image.
    static let kDefaultUnitWidth: CGFloat = 0.9

    // The max width of the text as a fraction of the image width.
    //
    // This provides continuity of text layout before/after cropping.
    //
    // NOTE: When you scale the text with with a pinch gesture, that
    // affects _scaling_, not the _unit width_, since we don't want
    // to change how the text wraps when scaling.
    let unitWidth: CGFloat

    // 0 = no rotation.
    // CGFloat.pi * 0.5 = rotation 90 degrees clockwise.
    let rotationRadians: CGFloat

    static let kMaxScaling: CGFloat = 4.0

    static let kMinScaling: CGFloat = 0.5

    let scaling: CGFloat

    init(text: String,
         color: ColorPickerBarColor,
         fontSize: CGFloat,
         textStyle: MediaTextView.TextStyle = .regular,
         decorationStyle: MediaTextView.DecorationStyle = .none,
         fontReferenceImageWidth: CGFloat,
         unitCenter: ImageEditorSample = ImageEditorSample(x: 0.5, y: 0.5),
         unitWidth: CGFloat = ImageEditorTextItem.kDefaultUnitWidth,
         rotationRadians: CGFloat = 0.0,
         scaling: CGFloat = 1.0) {
        self.text = text
        self.color = color
        self.fontSize = fontSize
        self.textStyle = textStyle
        self.decorationStyle = decorationStyle
        self.fontReferenceImageWidth = fontReferenceImageWidth
        self.unitCenter = unitCenter
        self.unitWidth = unitWidth
        self.rotationRadians = rotationRadians
        self.scaling = scaling

        super.init(itemType: .text)
    }

    init(
        itemId: String,
        text: String,
        color: ColorPickerBarColor,
        fontSize: CGFloat,
        textStyle: MediaTextView.TextStyle,
        decorationStyle: MediaTextView.DecorationStyle,
        fontReferenceImageWidth: CGFloat,
        unitCenter: ImageEditorSample,
        unitWidth: CGFloat,
        rotationRadians: CGFloat,
        scaling: CGFloat
    ) {
        self.text = text
        self.color = color
        self.fontSize = fontSize
        self.textStyle = textStyle
        self.decorationStyle = decorationStyle
        self.fontReferenceImageWidth = fontReferenceImageWidth
        self.unitCenter = unitCenter
        self.unitWidth = unitWidth
        self.rotationRadians = rotationRadians
        self.scaling = scaling

        super.init(itemId: itemId, itemType: .text)
    }

    class func empty(withColor color: ColorPickerBarColor,
                     textStyle: MediaTextView.TextStyle,
                     decorationStyle: MediaTextView.DecorationStyle,
                     unitWidth: CGFloat,
                     fontReferenceImageWidth: CGFloat,
                     scaling: CGFloat,
                     rotationRadians: CGFloat) -> ImageEditorTextItem {
        return ImageEditorTextItem(text: "",
                                   color: color,
                                   fontSize: ImageEditorTextItem.defaultFontSize,
                                   textStyle: textStyle,
                                   decorationStyle: decorationStyle,
                                   fontReferenceImageWidth: fontReferenceImageWidth,
                                   unitWidth: unitWidth,
                                   rotationRadians: rotationRadians,
                                   scaling: scaling)
    }

    func copy(withText newText: String, color newColor: ColorPickerBarColor) -> ImageEditorTextItem {
        return ImageEditorTextItem(itemId: itemId,
                                   text: newText,
                                   color: newColor,
                                   fontSize: fontSize,
                                   textStyle: textStyle,
                                   decorationStyle: decorationStyle,
                                   fontReferenceImageWidth: fontReferenceImageWidth,
                                   unitCenter: unitCenter,
                                   unitWidth: unitWidth,
                                   rotationRadians: rotationRadians,
                                   scaling: scaling)
    }

    func copy(unitCenter: CGPoint) -> ImageEditorTextItem {
        return ImageEditorTextItem(itemId: itemId,
                                   text: text,
                                   color: color,
                                   fontSize: fontSize,
                                   textStyle: textStyle,
                                   decorationStyle: decorationStyle,
                                   fontReferenceImageWidth: fontReferenceImageWidth,
                                   unitCenter: unitCenter,
                                   unitWidth: unitWidth,
                                   rotationRadians: rotationRadians,
                                   scaling: scaling)
    }

    func copy(scaling: CGFloat, rotationRadians: CGFloat) -> ImageEditorTextItem {
        return ImageEditorTextItem(itemId: itemId,
                                   text: text,
                                   color: color,
                                   fontSize: fontSize,
                                   textStyle: textStyle,
                                   decorationStyle: decorationStyle,
                                   fontReferenceImageWidth: fontReferenceImageWidth,
                                   unitCenter: unitCenter,
                                   unitWidth: unitWidth,
                                   rotationRadians: rotationRadians,
                                   scaling: scaling)
    }

    func copy(unitWidth: CGFloat) -> ImageEditorTextItem {
        return ImageEditorTextItem(itemId: itemId,
                                   text: text,
                                   color: color,
                                   fontSize: fontSize,
                                   textStyle: textStyle,
                                   decorationStyle: decorationStyle,
                                   fontReferenceImageWidth: fontReferenceImageWidth,
                                   unitCenter: unitCenter,
                                   unitWidth: unitWidth,
                                   rotationRadians: rotationRadians,
                                   scaling: scaling)
    }

    func copy(fontSize: CGFloat) -> ImageEditorTextItem {
        return ImageEditorTextItem(itemId: itemId,
                                   text: text,
                                   color: color,
                                   fontSize: fontSize,
                                   textStyle: textStyle,
                                   decorationStyle: decorationStyle,
                                   fontReferenceImageWidth: fontReferenceImageWidth,
                                   unitCenter: unitCenter,
                                   unitWidth: unitWidth,
                                   rotationRadians: rotationRadians,
                                   scaling: scaling)
    }

    func copy(color: ColorPickerBarColor) -> ImageEditorTextItem {
        return ImageEditorTextItem(itemId: itemId,
                                   text: text,
                                   color: color,
                                   fontSize: fontSize,
                                   textStyle: textStyle,
                                   decorationStyle: decorationStyle,
                                   fontReferenceImageWidth: fontReferenceImageWidth,
                                   unitCenter: unitCenter,
                                   unitWidth: unitWidth,
                                   rotationRadians: rotationRadians,
                                   scaling: scaling)
    }

    func copy(textStyle: MediaTextView.TextStyle, decorationStyle: MediaTextView.DecorationStyle) -> ImageEditorTextItem {
        return ImageEditorTextItem(itemId: itemId,
                                   text: text,
                                   color: color,
                                   fontSize: fontSize,
                                   textStyle: textStyle,
                                   decorationStyle: decorationStyle,
                                   fontReferenceImageWidth: fontReferenceImageWidth,
                                   unitCenter: unitCenter,
                                   unitWidth: unitWidth,
                                   rotationRadians: rotationRadians,
                                   scaling: scaling)
    }

    override func outputScale() -> CGFloat {
        return scaling
    }

    static func == (left: ImageEditorTextItem, right: ImageEditorTextItem) -> Bool {
        return (left.text == right.text &&
                left.color == right.color &&
                left.textStyle == right.textStyle &&
                left.decorationStyle == right.decorationStyle &&
                left.fontSize.fuzzyEquals(right.fontSize) &&
                left.fontReferenceImageWidth.fuzzyEquals(right.fontReferenceImageWidth) &&
                left.unitCenter.fuzzyEquals(right.unitCenter) &&
                left.unitWidth.fuzzyEquals(right.unitWidth) &&
                left.rotationRadians.fuzzyEquals(right.rotationRadians) &&
                left.scaling.fuzzyEquals(right.scaling))
    }
}
