using System;
using System.Collections.Generic;
using Tesseract;

namespace Ocr.Tesseract.Models;

/// <summary>
/// Model for storing scanned words
/// </summary>
public class Word : Choice, IEquatable<Word>
{
  /// <summary>
  /// Determines whether the <see cref="Word"/> was found in the dictionary
  /// </summary>
  public bool IsFromDictionary { get; set; }

  /// <summary>
  /// Determines whether the <see cref="Word"/> is numeric
  /// </summary>
  public bool Numeric { get; set; }

  /// <summary>
  /// Other available <see cref="Choice"/>s for this <see cref="Word"/>
  /// </summary>
  public ICollection<Choice> Choices { get; set; } = Array.Empty<Choice>();

  /// <inheritdoc />
  public Word(
    string text,
    float confidence,
    bool isFromDictionary,
    bool isNumeric
  )
    : base(text, confidence)
  {
    Text = text;
    Confidence = confidence;
    IsFromDictionary = isFromDictionary;
    Numeric = isNumeric;
  }

  public static Word Parse(ResultIterator iterator)
  {
    var word = new Word
    (
      iterator.GetText(PageIteratorLevel.Word),
      iterator.GetConfidence(PageIteratorLevel.Word),
      iterator.GetWordIsFromDictionary(),
      iterator.GetWordIsNumeric()
    );

    if (string.IsNullOrEmpty(word.Text))
    {
      // No choices available
      return word;
    }

    var choices = new List<Choice>();
    using var choiceIterator = iterator.GetChoiceIterator();
    do
    {
      choices.Add(Parse(choiceIterator));
    } while (choiceIterator.Next());

    word.Choices = choices;
    return word;
  }

  #region Equality members

  /// <inheritdoc />
  public bool Equals(Word? other)
  {
    if (ReferenceEquals(null, other))
    {
      return false;
    }

    if (ReferenceEquals(this, other))
    {
      return true;
    }

    return base.Equals(other)
           && IsFromDictionary == other.IsFromDictionary
           && Numeric == other.Numeric;
  }

  /// <inheritdoc />
  public override bool Equals(object? obj)
  {
    if (ReferenceEquals(null, obj))
    {
      return false;
    }

    if (ReferenceEquals(this, obj))
    {
      return true;
    }

    if (obj.GetType() != this.GetType())
    {
      return false;
    }

    return Equals((Word)obj);
  }

  /// <inheritdoc />
  public override int GetHashCode()
  {
    return HashCode.Combine(base.GetHashCode(), IsFromDictionary, Numeric);
  }

  public static bool operator ==(Word? left, Word? right)
  {
    return Equals(left, right);
  }

  public static bool operator !=(Word? left, Word? right)
  {
    return !Equals(left, right);
  }

  #endregion
}
