using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;

using CommonHelpers.WASM;
using AFXUI;

namespace BookRegistry.Client.Shared {

//**************************************************************************************************
/// <summary>
/// Edit-box control (application components lib.)
/// </summary>
public partial class AEditBox: AComponent {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public ElementReference? InputElement { get; protected set; }
protected AErrorFrame? ErrorFrame;
protected AResetControl? ResetControl;
//==================================================================================================

public static string? DefaultInputClasses;// = "form-control-sm";
public static string? DefaultInputStyle;
public static string? DefaultContainerStyle = "display: inline";
public static ElementPresence DefaultResetPresence = ElementPresence.None;

public static string ResetHRefValue => "#reset-text";
public static string ResetHRefTitle => "Clear input field";

[Parameter] public string? ContainerId { get; set; }
[Parameter] public string? ContainerClasses { get; set; }
[Parameter] public string? ContainerStyle_0 { get; set; } = DefaultContainerStyle;
[Parameter] public string? ContainerStyle { get; set; }
[Parameter] public bool IsPasswordEditor { get; set; }
[Parameter] public string? InputId { get; set; }
[Parameter] public string? InputClasses_0 { get; set; } = DefaultInputClasses;
[Parameter] public string? InputClasses { get; set; }
[Parameter] public string? InputStyle_0 { get; set; } = DefaultInputStyle;
[Parameter] public string? InputStyle { get; set; }
[Parameter] public string? ResetHRefId { get; set; }
[Parameter] public string? ResetHRefClasses { get; set; }
[Parameter] public string? ResetHRefStyle { get; set; }
[Parameter] public string? Title { get; set; }
[Parameter] public string? AccessKey { get; set; }
[Parameter] public int? TabIndex { get; set; }
[Parameter] public bool Enabled { get; set; } = true;
[Parameter] public ElementPresence ResetPresence { get; set; } = DefaultResetPresence;
[Parameter] public string? InitialText { get; set; }
[Parameter] public string? Placeholder { get; set; }

[Parameter] public Action? ON_SetText { get; set; }
[Parameter] public Action? ON_Input { get; set; }
[Parameter] public Action? ON_ChangeExit { get; set; }
[Parameter] public Action<KeyboardEventArgs>? ON_KeyDown { get; set; }
[Parameter] public Action<MouseEventArgs>? ON_MouseDown { get; set; }
[Parameter] public Action<MouseEventArgs>? ON_MouseUp { get; set; }
[Parameter] public Action? ON_Focus { get; set; }
[Parameter] public Action? ON_Blur { get; set; }

[Parameter] public EventCallback ON_SetText_A { get; set; }
[Parameter] public EventCallback ON_Input_A { get; set; }
[Parameter] public EventCallback ON_ChangeExit_A { get; set; }
[Parameter] public EventCallback<KeyboardEventArgs> ON_KeyDown_A { get; set; }
[Parameter] public EventCallback<MouseEventArgs> ON_MouseDown_A { get; set; }
[Parameter] public EventCallback<MouseEventArgs> ON_MouseUp_A { get; set; }
[Parameter] public EventCallback ON_Focus_A { get; set; }
[Parameter] public EventCallback ON_Blur_A { get; set; }

[Parameter] public InputValidationCallback_Text? InputValidationCallback { get; set; }

[Parameter] public string? InitialErrorMessage { get; set; }

[Parameter(CaptureUnmatchedValues=true)]
public Dictionary<string,object>? Attributes { get; set; }

public AResetControl? OuterResetControl { get; set; }

//--------------------------------------------------------------------------------------------------

public string Text {
   get {
      _Text_ = GetText();
      return IsPasswordEditor ? _Text_ : _Text_.TrimEnd();
   }
   set => SetText( value );
}
string GetText() =>
   JS.Invoke<string?>( "AEditBox_GetText", InputElement ) ?? "";
void DoSetText(string? value)
{
   if ( value!=null && !IsPasswordEditor )
      value = value.TrimEnd();
   JS.Invoke( "AEditBox_SetText", InputElement, _Text_ = value ?? "" );
}

public bool HasText => Text != "";

//--------------------------------------------------------------------------------------------------

/// <exception cref="InvalidOperationException" />
public string? ErrorMessage {
   get => ErrorFrameNN.Message;
   set => ErrorFrameNN.Message = value;
}
/// <exception cref="InvalidOperationException" />
public bool HasError => ErrorFrameNN.HasError;

//==================================================================================================

public bool SetText(string? text,bool validateInputOnChange=true)
{
   string previousText = GetText();
   DoSetText( text );
   if ( validateInputOnChange && previousText != GetText() )
      ValidateInput();
   OnSetText_Invoke();
   return HasText;
}

public void Clear(bool validateInputOnChange=true) =>
   SetText( "", validateInputOnChange );

//--------------------------------------------------------------------------------------------------

/// <exception cref="InvalidOperationException" />
public void SetError(string message) => ErrorFrameNN.SetError( message );
/// <exception cref="InvalidOperationException" />
public void ResetError() => ErrorFrameNN.ResetError();

public bool ValidateInput() =>
   (ErrorMessage = InputValidationCallback?.Invoke(Text)) == null;

//--------------------------------------------------------------------------------------------------

public bool Focus() =>
   Enabled ? InputElement.Focus() : false;

public bool DeferredFocus() =>
   Enabled ? InputElement.DeferredFocus() : false;
public bool DeferredFocus(int milliseconds) =>
   Enabled ? InputElement.DeferredFocus( milliseconds ) : false;

//--------------------------------------------------------------------------------------------------

public bool Select() =>
   InputElement.Select();

//==================================================================================================

protected string? TextValue => _Text_;

//--------------------------------------------------------------------------------------------------

protected string InputElement_EDClass =>
   Enabled ? "a_editbox_EnabledINPUT" : "a_editbox_DisabledINPUT";

protected string? ResetControlStyle =>
   ResetPresence != ElementPresence.PlaceOnly ?
      ResetHRefStyle : "visibility: hidden" +
      (!string.IsNullOrWhiteSpace( ResetHRefStyle ) ? "; "+ResetHRefStyle : "");

/// <exception cref="InvalidOperationException" />
protected AErrorFrame ErrorFrameNN =>
   ErrorFrame ?? throw new InvalidOperationException(
      "\"ErrorFrameNN\"-property is not available (\"AEditBox\").");

//--------------------------------------------------------------------------------------------------

protected override void OnInitialized() =>
go_nt(()=>{
   base.OnInitialized();
   UMagistral.OnPopModalPopup += ResetSelectionFlags;
});

protected override void OnAfterRender(bool firstRender) =>
go_nt(()=>{
   base.OnAfterRender( firstRender );
   if ( ResetControl != null )
      ResetControl.Target = this;
});

protected override void On_FirstParametersSet() =>
   _Text_ = InitialText;

//--------------------------------------------------------------------------------------------------

protected override void OnDispose() =>
go_nt(()=>{
   UMagistral.OnPopModalPopup -= ResetSelectionFlags;
   base.OnDispose();
});

//==================================================================================================

void InputElement_OnInput() =>
go_nt(()=>{
   HandleInputChanged();
   OnInput_Invoke();
});
void InputElement_OnChange() =>
go_nt(()=>{
   HandleInputChanged();
   OnChangeExit_Invoke();
});

void InputElement_OnMouseDown(MouseEventArgs e) =>
go_nt(()=>{
   _MouseDownJustReceived_ = true;
   OnMouseDown_Invoke( e );
});
void InputElement_OnMouseUp(MouseEventArgs e) =>
go_nt(()=>{
   if ( _SelectOnMouseUp_ )
   {
      InputElement.DeferredSelect();
      _SelectOnMouseUp_ = false;
   }
   OnMouseUp_Invoke( e );
});

void InputElement_OnKeyDown(KeyboardEventArgs e) =>
go_nt(()=>{
   if ( !Enabled )
      return;
   if ( HOps.IsOneOf( e.Key, "Delete", "Del" ) &&
        e.CtrlKey && !e.AltKey && !e.ShiftKey )
      ResetManually();
   OnKeyDown_Invoke( e );
});

void InputElement_OnFocus() =>
go_nt(()=>{
   InputElement.Select();
   if ( _MouseDownJustReceived_ )
   {
      _SelectOnMouseUp_ = true;
      _MouseDownJustReceived_ = false;
   }
   OnFocus_Invoke();
});
void InputElement_OnBlur() =>
go_nt(()=>{
   if ( HString.EndsWithWhiteSpace( _Text_ ) )
   {
      bool hadError = HasError;
      DoSetText( Text );
      if ( hadError )
         HandleInputChanged();
   }
   OnBlur_Invoke();
});

//--------------------------------------------------------------------------------------------------

void HandleInputChanged()
{
   string text = Text;
   OnSetText_Invoke();
   ValidateInput();
}

//--------------------------------------------------------------------------------------------------

void ResetManually()
{
   string previousText = GetText();
   Clear();
   if ( previousText != _Text_ )
      OnInput_Invoke();
}

bool ResetClicked()
{
   if ( !Enabled )
      return false;
   ResetManually();
   InputElement.Focus();
   return true;
}

//==================================================================================================

public override bool IsEnabled =>
   Enabled && InputElement.IsEnabled();

//--------------------------------------------------------------------------------------------------

public override ElementReference? MainElement { get => InputElement; }
public override ElementReference? AdditionalElement { get => ResetControl?.MainElement; }

//==================================================================================================

public override string? Interop_ErrorMessage => ErrorMessage;

public override AResetControl? Interop_OuterResetControl
{
   get => OuterResetControl;
   set => OuterResetControl = value;
}

public override bool Interop_Focus() =>
   Focus();
public override bool Interop_DeferredFocus() =>
   DeferredFocus();
public override bool Interop_Reset() =>
   ResetClicked();

//==================================================================================================

[JSInvokable]
public static void wasm_AEditBox_OnChange(string uniqueId) =>
   GetByUniqueId<AEditBox>( uniqueId )?.InputElement_OnChange();

[JSInvokable]
public static void wasm_AEditBox_OnBlur(string uniqueId) =>
   GetByUniqueId<AEditBox>( uniqueId )?.InputElement_OnBlur();

//==================================================================================================

void ResetSelectionFlags() =>
   _MouseDownJustReceived_ = _SelectOnMouseUp_ = false;

//==================================================================================================

void OnSetText_Invoke()
{
   ON_SetText?.Invoke();
   ON_SetText_A.InvokeAsync( null );
}
void OnInput_Invoke()
{
   ON_Input?.Invoke();
   ON_Input_A.InvokeAsync( null );
}
void OnChangeExit_Invoke()
{
   ON_ChangeExit?.Invoke();
   ON_ChangeExit_A.InvokeAsync( null );
}
void OnKeyDown_Invoke(KeyboardEventArgs e)
{
   ON_KeyDown?.Invoke( e );
   ON_KeyDown_A.InvokeAsync( e );
}
void OnMouseDown_Invoke(MouseEventArgs e)
{
   ON_MouseDown?.Invoke( e );
   ON_MouseDown_A.InvokeAsync( e );
}
void OnMouseUp_Invoke(MouseEventArgs e)
{
   ON_MouseUp?.Invoke( e );
   ON_MouseUp_A.InvokeAsync( e );
}
void OnFocus_Invoke()
{
   ON_Focus?.Invoke();
   ON_Focus_A.InvokeAsync( null );
}
void OnBlur_Invoke()
{
   ON_Blur?.Invoke();
   ON_Blur_A.InvokeAsync( null );
}

//==================================================================================================

string? _Text_;
bool _MouseDownJustReceived_, _SelectOnMouseUp_;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // AEditBox
//**************************************************************************************************

} // BookRegistry.Client.Shared
