using RobotNet.MapShares.Dtos;
using RobotNet.RobotManager.Services.OpenACS;
using RobotNet.RobotManager.Services.Planner.Space;
using RobotNet.RobotManager.Services.Traffic;
using RobotNet.RobotShares.Dtos;
using RobotNet.RobotShares.Enums;
using RobotNet.RobotShares.VDA5050.Order;
using RobotNet.RobotShares.VDA5050.State;
using RobotNet.Shares;
using SixLabors.ImageSharp;
using Action = RobotNet.RobotShares.VDA5050.InstantAction.Action;

namespace RobotNet.RobotManager.Services.Robot;

public class RobotOrder : IRobotOrder, IDisposable
{
    public bool IsError { get; private set; }
    public bool IsCompleted { get; private set; }
    public bool IsProcessing => !IsDisposed;
    public bool IsCanceled { get; private set; }
    public string[] Errors => [.. GetError()];
    public Guid MapId { get; private set; }
    public TrafficSolutionState TrafficSolutionState { get; private set; } = TrafficSolutionState.None;
    public NavigationPathEdge[] FullPath => GetFullPath();
    public NavigationPathEdge[] BasePath => GetBasePath();
    public List<ZoneDto> CurrentZones { get; set; } = [];

    private const int intervalTime = 50;
    private readonly WatchTimerAsync<RobotOrder> Timer;
    private int TimerCounter = 0;

    private OrderMsg OrderMsg;
    private readonly LoggerController<RobotOrder> Logger;
    private readonly TrafficManager TrafficManager;
    private readonly TrafficACS TrafficACS;
    private readonly MapManager MapManager;
    private readonly Func<OrderMsg, Task<MessageResult>> PubOrder;
    private readonly List<NodeDto> Nodes;
    private readonly List<EdgeDto> Edges;
    private readonly Dictionary<Guid, ZoneDto[]> Zones;
    private readonly IDictionary<string, IEnumerable<Action>> Actions;
    private readonly IRobotController RobotController;
    private readonly NavigationType NavigationType;
    private Guid TrafficManagerGoalId = Guid.Empty;
    private Guid TrafficACSGoalId = Guid.Empty;

    private bool IsDisposed = false;
    private bool IsWaittingCancel = false;
    private Guid CurrentBaseId = Guid.Empty;
    private Guid LastNodeId = Guid.Empty;

    public RobotOrder(NodeDto[] nodes,
                      EdgeDto[] edges,
                      Dictionary<Guid, ZoneDto[]> zones,
                      IDictionary<string, IEnumerable<Action>> actions,
                      OrderMsg orderMsg,
                      IRobotController robotController,
                      NavigationType navigationType,
                      TrafficManager traffiManager,
                      TrafficACS trafficACS,
                      MapManager mapManager,
                      LoggerController<RobotOrder> logger,
                      Func<OrderMsg, Task<MessageResult>> pubOrder)
    {
        if (nodes.Length < 2)
        {
            IsError = true;
            IsDisposed = true;
            throw new ArgumentException("Đường dẫn không hợp lệ. Số lượng nodes nhỏ hơn 2");
        }
        if (nodes.Length < 1)
        {
            IsError = true;
            IsDisposed = true;
            throw new ArgumentException("Đường dẫn không hợp lệ. Số lượng edges nhỏ hơn 1");
        }
        Nodes = [.. nodes];
        Edges = [.. edges];
        Zones = zones ?? [];
        MapId = Nodes[1].MapId;
        Actions = actions;
        OrderMsg = orderMsg;
        RobotController = robotController;
        NavigationType = navigationType;
        TrafficManager = traffiManager;
        TrafficACS = trafficACS;
        MapManager = mapManager;
        Logger = logger;
        PubOrder = pubOrder;
        Timer = new(intervalTime, TimerHandler, logger);
        Timer.Start();
    }

    private async Task<bool> PublishOrder(Guid goalId)
    {
        OrderMsg.OrderUpdateId++;
        OrderMsg.HeaderId++;
        OrderMsg.Timestamp = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
        var pubOrder = await PubOrder.Invoke(OrderMsg);
        if (!pubOrder.IsSuccess)
        {
            OrderMsg.OrderUpdateId--;
            OrderMsg.HeaderId--;
            return false;
        }
        CurrentBaseId = goalId;
        return true;
    }

    private void UpdatePath(NodeDto[] nodes, EdgeDto[] edges)
    {
        try
        {
            var map = Task.Run(async () => await MapManager.GetMapData(MapId));
            map.Wait();
            if (map.Result is null) Logger.Warning($"{RobotController.StateMsg.SerialNumber} - Robot Order cập nhật tuyến đường mới có lỗi xảy ra: không thể lấy dữ liệu bản đồ {MapId}");
            else if (!map.Result.IsSuccess) Logger.Warning($"{RobotController.StateMsg.SerialNumber} - Robot Order cập nhật tuyến đường mới có lỗi xảy ra: {map.Result.Message}");
            else if (map.Result.Data is not null)
            {
                Robot.RobotController.CreateOrderMsg(ref OrderMsg, nodes, edges, map.Result.Data, Actions, NavigationType, TrafficManager.Enable);
                Nodes.Clear();
                Nodes.AddRange(nodes);
                Edges.Clear();
                Edges.AddRange(edges);
            }
            else Logger.Warning($"{RobotController.StateMsg.SerialNumber} - Robot Order cập nhật tuyến đường mới có lỗi xảy ra: dữ liệu bản đồ {MapId} rỗng");
        }
        catch (Exception ex)
        {
            Logger.Warning($"{RobotController.StateMsg.SerialNumber} - Robot Order cập nhật tuyến đường mới có lỗi xảy ra: {ex}");
        }
    }

    private bool UpdateGoal(Guid goalId)
    {
        var currentNodeOrderIndex = Array.FindIndex(OrderMsg.Nodes, n => n.NodeId == LastNodeId.ToString());
        if (currentNodeOrderIndex != -1)
        {
            OrderMsg.Nodes = [.. OrderMsg.Nodes.Skip(currentNodeOrderIndex)];
            OrderMsg.Edges = [.. OrderMsg.Edges.Skip(currentNodeOrderIndex)];
        }

        var goalOrder = OrderMsg.Nodes.FirstOrDefault(n => n.NodeId == goalId.ToString());
        if (goalOrder is not null && CurrentBaseId.ToString() != goalOrder.NodeId)
        {
            for (int i = 0; i <= Array.IndexOf(OrderMsg.Nodes, goalOrder); i++)
            {
                OrderMsg.Nodes[i].Released = true;
            }
            for (int i = 0; i < Array.IndexOf(OrderMsg.Nodes, goalOrder); i++)
            {
                OrderMsg.Edges[i].Released = true;
            }
            return true;
        }
        return false;
    }

    private void HandleGiveWayState(TrafficSolution solution)
    {
        if (solution.GivewayNodes[^1].Id != solution.ReleaseNode.Id) return;
        if (solution.GivewayEdges.Count == 0) Logger.Warning($"{RobotController.StateMsg.SerialNumber} - Robot Order xử lí tránh đường lỗi: Không có edge mới nhận được");
        else if (solution.GivewayNodes.Count <= 1) Logger.Warning($"{RobotController.StateMsg.SerialNumber} - Robot Order xử lí tránh đường lỗi: nodes mới nhận được có số lượng {solution.Nodes.Length}");
        else if (CurrentBaseId != solution.ReleaseNode.Id)
        {
            List<NodeDto> nodes = [..solution.GivewayNodes.Select(n => new NodeDto()
            {
                Id = n.Id,
                Name = n.Name,
                X = n.X,
                Y = n.Y,
                Direction = MapCompute.GetNodeDirection(n.Direction),
            })];
            List<EdgeDto> edges = [..solution.GivewayEdges.Select(e => new EdgeDto()
            {
                Id = e.Id,
                ControlPoint1X = e.ControlPoint1X,
                ControlPoint2X = e.ControlPoint2X,
                ControlPoint1Y = e.ControlPoint1Y,
                ControlPoint2Y = e.ControlPoint2Y,
                TrajectoryDegree = e.TrajectoryDegree,
                StartNodeId = e.StartNodeId,
                EndNodeId = e.EndNodeId,
            })];
            nodes.Add(nodes[^2]);
            edges.Add(new()
            {
                Id = edges[^1].Id,
                StartNodeId = nodes[^2].Id,
                EndNodeId = nodes[^1].Id,
                ControlPoint1X = edges[^1].ControlPoint2X,
                ControlPoint1Y = edges[^1].ControlPoint2Y,
                ControlPoint2X = edges[^1].ControlPoint1X,
                ControlPoint2Y = edges[^1].ControlPoint1Y,
                TrajectoryDegree = edges[^1].TrajectoryDegree,
            });

            UpdatePath([.. nodes], [.. edges]);
        }
    }

    private void HandleRefreshPath(TrafficSolution solution)
    {
        if (solution.Edges.Length == 0) Logger.Warning($"{RobotController.StateMsg.SerialNumber} - Robot Order xử lí làm mới đường lỗi: Không có edge mới nhận được");
        else if (solution.Nodes.Length <= 1) Logger.Warning($"{RobotController.StateMsg.SerialNumber} - Robot Order xử lí làm mới đường lỗi: nodes mới nhận được có số lượng {solution.Nodes.Length}");
        else if (solution.Nodes[^1].Id != Nodes[^1].Id) Logger.Warning($"{RobotController.StateMsg.SerialNumber} - Robot Order xử lí làm mới đường lỗi: tuyến đường mới không kết thúc tại đích cũ");
        else
        {
            List<NodeDto> nodes = [..solution.Nodes.Select(n => new NodeDto()
            {
                Id = n.Id,
                Name = n.Name,
                X = n.X,
                Y = n.Y,
                Direction = MapCompute.GetNodeDirection(n.Direction),
            })];
            List<EdgeDto> edges = [..solution.Edges.Select(e => new EdgeDto()
            {
                Id = e.Id,
                StartNodeId = e.StartNodeId,
                EndNodeId = e.EndNodeId,
                ControlPoint1X = e.ControlPoint1X,
                ControlPoint1Y = e.ControlPoint1Y,
                ControlPoint2X = e.ControlPoint2X,
                ControlPoint2Y = e.ControlPoint2Y,
                TrajectoryDegree = e.TrajectoryDegree,
            })];

            UpdatePath([.. nodes], [.. edges]);
        }
    }

    private async Task<Guid> RequestInACS(Dictionary<Guid, ZoneDto[]> newZones)
    {
        Guid trafficACSrelaseNodeId = Guid.Empty;
        foreach (var zone in newZones)
        {
            if (zone.Value.Length == 0) trafficACSrelaseNodeId = zone.Key;
            else
            {
                bool requestSuccess = true;
                foreach (var zoneACS in zone.Value)
                {
                    if (string.IsNullOrEmpty(zoneACS.Name)) continue;
                    if (CurrentZones.Any(z => z.Id == zoneACS.Id)) continue;

                    var getTrafficACS = await TrafficACS.RequestIn(RobotController.SerialNumber, zoneACS.Name);
                    if (getTrafficACS.IsSuccess && getTrafficACS.Data) CurrentZones.Add(zoneACS);
                    else
                    {
                        requestSuccess = false;
                        break;
                    }
                }
                if (requestSuccess) trafficACSrelaseNodeId = zone.Key;
                else break;
            }
        }
        return trafficACSrelaseNodeId;
    }

    private async Task UpdateTraffic()
    {
        var trafficManagerGoalIndex = Nodes.FindIndex(n => n.Id == TrafficManagerGoalId);
        var trafficACSGoalIndex = Nodes.FindIndex(n => n.Id == TrafficACSGoalId);
        if (trafficManagerGoalIndex != -1 && trafficACSGoalIndex != -1)
        {
            var goalIndex = Math.Min(trafficManagerGoalIndex, trafficACSGoalIndex);
            NodeDto goal = Nodes[goalIndex];
            var updatemsg = UpdateGoal(Nodes[goalIndex].Id);
            if (updatemsg && Nodes[goalIndex].Id != CurrentBaseId)
            {
                var publish = await PublishOrder(Nodes[goalIndex].Id);
                if (!publish) Logger.Warning($"{RobotController.StateMsg.SerialNumber} - Robot Order publish xảy ra lỗi");
            }
        }
    }

    private async Task GoOutTrafficACS(Guid lastNodeId)
    {
        if (CurrentBaseId == Guid.Empty) return;
        var goalIndex = Nodes.FindIndex(n => n.Id == CurrentBaseId);
        if (goalIndex == -1) return;
        var inNodeIndex = Nodes.FindIndex(n => n.Id == lastNodeId);
        inNodeIndex = inNodeIndex != -1 ? inNodeIndex : 0;
        if (goalIndex <= inNodeIndex) return;
        var baseNodes = Nodes.GetRange(inNodeIndex, goalIndex + 1 - inNodeIndex);
        var baseZones = PathPlanner.GetZones([.. baseNodes], Zones);
        var outZones = CurrentZones.Where(z => !baseZones.Any(bz => bz.Id == z.Id)).ToList();
        foreach (var zoneACS in outZones)
        {
            if (string.IsNullOrEmpty(zoneACS.Name)) continue;
            var outTrafficACS = await TrafficACS.RequestOut(RobotController.SerialNumber, zoneACS.Name);
            if (outTrafficACS.IsSuccess && outTrafficACS.Data) CurrentZones.RemoveAll(z => z.Id == zoneACS.Id);
        }
    }

    private Guid GetLastNodeId()
    {
        Guid lastNodeId = Guid.Empty;
        double minDeviationDistance = TrafficACS.DeviationDistance;
        foreach (var node in Nodes)
        {
            var distance = Math.Sqrt(Math.Pow(RobotController.VisualizationMsg.AgvPosition.X - node.X, 2) + Math.Pow(RobotController.VisualizationMsg.AgvPosition.Y - node.Y, 2));
            if (distance < minDeviationDistance)
            {
                minDeviationDistance = distance;
                lastNodeId = node.Id;
            }
        }
        return lastNodeId;
    }

    private async Task TimerHandler()
    {
        try
        {
            var lastNodeId = GetLastNodeId();
            if (lastNodeId != Guid.Empty && LastNodeId != lastNodeId) LastNodeId = lastNodeId;

            if (TimerCounter++ > 10)
            {
                TimerCounter = 0;
                if (IsOrderClear())
                {
                    if (RobotController.StateMsg.LastNodeId == Nodes[^1].Id.ToString() && GetActionFinished())
                    {
                        IsCompleted = true;
                        Logger.Info($"{RobotController.StateMsg.SerialNumber} - Robot Order đã hoàn thành tới đích {Nodes[^1].Name}");
                    }
                    else if (RobotController.StateMsg.LastNodeId != Nodes[^1].Id.ToString() || GetActionFailed())
                    {
                        Logger.Error($"{RobotController.StateMsg.SerialNumber} - Robot Order kết thúc lỗi. {string.Join(", ", GetError())}");
                        IsError = true;
                    }
                }
                if (!RobotController.IsOnline)
                {
                    Logger.Error($"{RobotController.StateMsg.SerialNumber} - Robot Order kết thúc: robot mất kết nối");
                    IsError = true;
                }
                if (IsCompleted || IsError || (IsWaittingCancel && IsOrderClear()))
                {
                    Dispose();
                    return;
                }
                if (!IsWaittingCancel)
                {
                    await GoOutTrafficACS(LastNodeId);

                    if (!TrafficManager.Enable)
                    {
                        if (TrafficManagerGoalId != Nodes[^1].Id) TrafficManagerGoalId = Nodes[^1].Id;
                    }
                    else
                    {
                        TrafficManager.UpdateInNode(RobotController.SerialNumber, LastNodeId);
                        var trafficSolution = TrafficManager.GetTrafficNode(RobotController.SerialNumber);
                        switch (trafficSolution.State)
                        {
                            case TrafficSolutionState.GiveWay:
                                HandleGiveWayState(trafficSolution);
                                break;
                            case TrafficSolutionState.RefreshPath:
                                HandleRefreshPath(trafficSolution);
                                break;
                            case TrafficSolutionState.Complete:
                            case TrafficSolutionState.Waitting:
                            default:
                                break;
                        }
                        TrafficSolutionState = trafficSolution.State;
                        var goal = Nodes.FirstOrDefault(n => n.Id == trafficSolution.ReleaseNode.Id);
                        if (goal is not null && goal.Id != TrafficManagerGoalId) TrafficManagerGoalId = goal.Id;
                    }

                    if (!TrafficACS.Enable)
                    {
                        if (TrafficACSGoalId != Nodes[^1].Id) TrafficACSGoalId = Nodes[^1].Id;
                    }
                    else
                    {
                        var newZones = TrafficACS.GetZones(LastNodeId, [.. Nodes], Zones);
                        var trafficACSrelaseNodeId = await RequestInACS(newZones);
                        if (trafficACSrelaseNodeId != Guid.Empty && trafficACSrelaseNodeId != TrafficACSGoalId) TrafficACSGoalId = trafficACSrelaseNodeId;
                    }

                    await UpdateTraffic();
                }
            }
        }
        catch (Exception ex)
        {
            Logger.Error($"{RobotController.StateMsg.SerialNumber} - Robot Order Handler xảy ra lỗi: {ex}");
        }
    }

    private IEnumerable<string> GetError()
    {
        if (RobotController.StateMsg.Errors is not null && RobotController.StateMsg.Errors.Length > 0)
        {
            foreach (var error in RobotController.StateMsg.Errors)
            {
                yield return $"{error.ErrorType} - {error.ErrorDescription}";
            }
        }
        yield break;
    }

    private bool GetActionFinished()
    {
        if (RobotController.StateMsg.ActionStates is not null && RobotController.StateMsg.ActionStates.Length > 0)
        {
            var finalActions = OrderMsg.Nodes[^1].Actions;
            foreach (var actionState in RobotController.StateMsg.ActionStates)
            {
                if (finalActions.Any(a => a.ActionId == actionState.ActionId) && actionState.ActionStatus != ActionStatus.FINISHED.ToString()) return false;
            }
        }
        return true;
    }

    private bool GetActionFailed()
    {
        if (RobotController.StateMsg.ActionStates is not null && RobotController.StateMsg.ActionStates.Length > 0)
        {
            var finalActions = OrderMsg.Nodes[^1].Actions;
            foreach (var actionState in RobotController.StateMsg.ActionStates)
            {
                if (finalActions.Any(a => a.ActionId == actionState.ActionId) && actionState.ActionStatus == ActionStatus.FAILED.ToString()) return true;
            }
        }
        return false;
    }

    public async Task<bool> Cancel(CancellationToken cancellation)
    {
        try
        {
            IsWaittingCancel = true;
            while (!cancellation.IsCancellationRequested)
            {
                if (IsDisposed)
                {
                    IsCanceled = true;
                    return true;
                }
                if (Timer.Disposed) Dispose();
                try { await Task.Delay(500, cancellation); } catch { }
            }
            IsWaittingCancel = false;
            return false;
        }
        catch
        {
            IsWaittingCancel = false;
            return false;
        }
    }

    private NavigationPathEdge[] SplitChecking(NodeDto lastNode, NodeDto nearLastNode, EdgeDto edge)
    {
        List<NavigationPathEdge> pathEdges = [];
        var splitStartPath = PathPlanner.PathSplit([new NodeDto
                        {
                            Id = lastNode.Id,
                            X = lastNode.X,
                            Y = lastNode.Y,
                        }, new NodeDto
                        {
                            Id = nearLastNode.Id,
                            X = nearLastNode.X,
                            Y = nearLastNode.Y,
                        }], [edge], 0.1);
        if (splitStartPath.IsSuccess && splitStartPath.Data is not null)
        {
            int index = 0;
            double minDistance = double.MaxValue;
            for (int i = 0; i < splitStartPath.Data.Length; i++)
            {
                var distance = Math.Sqrt(Math.Pow(RobotController.VisualizationMsg.AgvPosition.X - splitStartPath.Data[i].X, 2) +
                                         Math.Pow(RobotController.VisualizationMsg.AgvPosition.Y - splitStartPath.Data[i].Y, 2));
                if (distance < minDistance)
                {
                    minDistance = distance;
                    index = i;
                }
            }
            for (int i = index; i < splitStartPath.Data.Length - 1; i++)
            {
                pathEdges.Add(new()
                {
                    StartX = splitStartPath.Data[i].X,
                    StartY = splitStartPath.Data[i].Y,
                    EndX = splitStartPath.Data[i + 1].X,
                    EndY = splitStartPath.Data[i + 1].Y,
                    Degree = 1,
                });
            }
        }
        return [.. pathEdges];
    }

    private NavigationPathEdge[] GetFullPath()
    {
        List<NavigationPathEdge> pathEdges = [];
        if (RobotController.StateMsg.NodeStates is not null && RobotController.StateMsg.NodeStates.Length > 0)
        {
            var lastNodeIndex = Nodes.FindIndex(n => n.Id == LastNodeId);
            lastNodeIndex = lastNodeIndex != -1 ? lastNodeIndex : 0;
            if (lastNodeIndex < Nodes.Count - 1) pathEdges = [.. SplitChecking(Nodes[lastNodeIndex], Nodes[lastNodeIndex + 1], Edges[lastNodeIndex])];
            if (lastNodeIndex < Nodes.Count - 2)
            {
                var nodes = Nodes.GetRange(lastNodeIndex + 1, Nodes.Count - lastNodeIndex - 1);
                var edges = Edges.GetRange(lastNodeIndex + 1, nodes.Count - 1);
                for (int i = 0; i < nodes.Count - 1; i++)
                {
                    pathEdges.Add(new()
                    {
                        StartX = nodes[i].X,
                        StartY = nodes[i].Y,
                        EndX = nodes[i + 1].X,
                        EndY = nodes[i + 1].Y,
                        ControlPoint1X = edges[i].ControlPoint1X,
                        ControlPoint1Y = edges[i].ControlPoint1Y,
                        ControlPoint2X = edges[i].ControlPoint2X,
                        ControlPoint2Y = edges[i].ControlPoint2Y,
                        Degree = edges[i].TrajectoryDegree == MapShares.Enums.TrajectoryDegree.One ? 1 : edges[i].TrajectoryDegree == MapShares.Enums.TrajectoryDegree.Two ? 2 : 3,
                    });
                }
            }
        }
        return [.. pathEdges];
    }

    private NavigationPathEdge[] GetBasePath()
    {
        List<NavigationPathEdge> pathEdges = [];
        if (RobotController.StateMsg.NodeStates is not null && RobotController.StateMsg.NodeStates.Length > 0)
        {
            var lastNodeIndex = Nodes.FindIndex(n => n.Id == LastNodeId);
            lastNodeIndex = lastNodeIndex != -1 ? lastNodeIndex : 0;
            var baseNodeIndex = Nodes.FindIndex(n => n.Id == CurrentBaseId);
            baseNodeIndex = baseNodeIndex != -1 ? baseNodeIndex : Nodes.Count - 1;

            if (lastNodeIndex < baseNodeIndex)
            {
                if (lastNodeIndex < Nodes.Count - 1) pathEdges = [.. SplitChecking(Nodes[lastNodeIndex], Nodes[lastNodeIndex + 1], Edges[lastNodeIndex])];
                if (lastNodeIndex < Nodes.Count - 2 && lastNodeIndex < baseNodeIndex - 1)
                {
                    var nodes = Nodes.GetRange(lastNodeIndex + 1, baseNodeIndex - lastNodeIndex);
                    if (nodes.Count > 1)
                    {
                        var edges = Edges.GetRange(lastNodeIndex + 1, nodes.Count - 1);
                        for (int i = 0; i < nodes.Count - 1; i++)
                        {
                            pathEdges.Add(new()
                            {
                                StartX = nodes[i].X,
                                StartY = nodes[i].Y,
                                EndX = nodes[i + 1].X,
                                EndY = nodes[i + 1].Y,
                                ControlPoint1X = edges[i].ControlPoint1X,
                                ControlPoint1Y = edges[i].ControlPoint1Y,
                                ControlPoint2X = edges[i].ControlPoint2X,
                                ControlPoint2Y = edges[i].ControlPoint2Y,
                                Degree = edges[i].TrajectoryDegree == MapShares.Enums.TrajectoryDegree.One ? 1 : edges[i].TrajectoryDegree == MapShares.Enums.TrajectoryDegree.Two ? 2 : 3,
                            });
                        }
                    }
                }
            }
        }
        return [.. pathEdges];
    }

    private bool IsOrderClear() => RobotController.StateMsg.NodeStates.Length == 0 && RobotController.StateMsg.EdgeStates.Length == 0;

    public void CreateComledted()
    {
        IsCompleted = true;
        IsCanceled = false;
        IsError = false;
    }

    public void Dispose()
    {
        IsDisposed = true;
        Timer.Dispose();
        TrafficManager.DeleteAgent(RobotController.SerialNumber);
        GC.SuppressFinalize(this);
    }
}
