package com.grigowashere.aismap.view;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

public abstract class BaseDockWidget extends FrameLayout {
    private static final String TAG = "BaseDockWidget";
    
    // Константы
    protected static final int CIRCLE_SIZE_DP = 120;
    protected static final int DEFAULT_DOCK_HEIGHT_DP = 80;
    protected static final float MIN_SCALE = 0.5f;
    protected static final float MAX_SCALE = 2.0f;
    protected static final float SCALE_STEP = 0.1f;
    
    // Состояние виджета
    protected boolean isDocked = true; // По умолчанию в dock-режиме
    protected boolean dockTop = true;
    protected boolean isMorphing = false;
    protected float morphProgress = 0.0f; // 0 = dock, 1 = circle
    
    // Перетаскивание
    protected boolean dragging = false;
    protected float dX, dY;
    protected Float targetDragX = null;
    protected Float targetDragY = null;
    
    // Изменение размера в dock режиме
    protected boolean resizingDock = false;
    protected float lastTouchY;
    protected int dockHeightPx = 0;
    
    // Масштабирование
    protected float scaleFactor = 1.0f;
    protected float initialDistance = 0;
    protected float initialScale = 1.0f;
    
    // Анимация
    protected ValueAnimator morphAnimator;
    
    // Интерфейс для уведомления об изменении размера
    public interface OnDockResizeListener {
        void onDockResize(int newHeight);
    }
    protected OnDockResizeListener dockResizeListener;
    
    // Интерфейс для уведомления об изменении состояния docked
    public interface OnDockStateChangeListener {
        void onDockStateChanged(boolean isDocked, boolean isTop);
    }
    protected OnDockStateChangeListener dockStateChangeListener;
    
    public BaseDockWidget(Context context) {
        super(context);
        init();
    }
    
    public BaseDockWidget(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    
    private void init() {
        setClickable(true);
        setFocusable(true);
        
        // Инициализируем в dock-режиме
        post(() -> {
            if (isDocked) {
                ViewGroup parent = (ViewGroup) getParent();
                if (parent != null) {
                    setX(0);
                    setY(0);
                    ViewGroup.LayoutParams lp = getLayoutParams();
                    lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
                    lp.height = (int) dp(DEFAULT_DOCK_HEIGHT_DP);
                    dockHeightPx = 0; // Сбрасываем сохраненную высоту
                    setLayoutParams(lp);
                }
            }
        });
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (isMorphing) return true;
        
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                return handleTouchDown(event);
            case MotionEvent.ACTION_MOVE:
                return handleTouchMove(event);
            case MotionEvent.ACTION_UP:
                return handleTouchUp(event);
            case MotionEvent.ACTION_POINTER_DOWN:
                return handlePointerDown(event);
            case MotionEvent.ACTION_POINTER_UP:
                return handlePointerUp(event);
        }
        return super.onTouchEvent(event);
    }
    
    private boolean handleTouchDown(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        
        if (isDocked) {
            // Проверяем зоны изменения размера в зависимости от позиции закрепления
            if (dockTop) {
                // Если закреплен сверху, зона изменения размера только снизу
                if (y > getHeight() - dp(24)) {
                    resizingDock = true;
                    lastTouchY = event.getRawY();
                    return true;
                }
            } else {
                // Если закреплен снизу, зона изменения размера только сверху
                if (y < dp(24)) {
                    resizingDock = true;
                    lastTouchY = event.getRawY();
                    return true;
                }
            }
            
            // Если нажали в центральной области dock-виджета, переводим в movable режим
            // Вычисляем новую позицию, чтобы виджет был под пальцем
            float newX = event.getRawX() - dp(CIRCLE_SIZE_DP) / 2;
            float newY = event.getRawY() - dp(CIRCLE_SIZE_DP) / 2;
            
            // Ограничиваем в пределах экрана
            ViewGroup parent = (ViewGroup) getParent();
            if (parent != null) {
                newX = Math.max(0, Math.min(newX, parent.getWidth() - dp(CIRCLE_SIZE_DP)));
                newY = Math.max(0, Math.min(newY, parent.getHeight() - dp(CIRCLE_SIZE_DP)));
            }
            
            setDocked(false, dockTop, newX, newY);
        }
        
        // Обычное перетаскивание
        dragging = true;
        dX = getX() - event.getRawX();
        dY = getY() - event.getRawY();
        
        return true;
    }
    
    private boolean handleTouchMove(MotionEvent event) {
        if (resizingDock) {
            handleDockResize(event);
            return true;
        }
        
        // Обработка масштабирования двумя пальцами
        if (event.getPointerCount() == 2 && initialDistance > 0) {
            float distance = getDistance(event);
            float scale = distance / initialDistance;
            scaleFactor = Math.max(MIN_SCALE, Math.min(MAX_SCALE, initialScale * scale));
            requestLayout();
            return true;
        }
        
        if (dragging) {
            float newX = event.getRawX() + dX;
            float newY = event.getRawY() + dY;
            
            // Ограничиваем движение в пределах родителя
            ViewGroup parent = (ViewGroup) getParent();
            if (parent != null) {
                newX = Math.max(0, Math.min(newX, parent.getWidth() - getWidth()));
                newY = Math.max(0, Math.min(newY, parent.getHeight() - getHeight()));
            }
            
            setX(newX);
            setY(newY);
            
            // Проверяем возможность докинга
            checkDocking(event);
            return true;
        }
        
        return false;
    }
    
    private boolean handleTouchUp(MotionEvent event) {
        if (resizingDock) {
            resizingDock = false;
            return true;
        }
        
        if (dragging) {
            dragging = false;
            
            // Если виджет находится в зоне докинга, доким его
            if (shouldDock(event)) {
                performDocking(event);
            }
            return true;
        }
        
        return false;
    }
    
    private boolean handlePointerDown(MotionEvent event) {
        if (event.getPointerCount() == 2) {
            initialDistance = getDistance(event);
            initialScale = scaleFactor;
        }
        return true;
    }
    
    private boolean handlePointerUp(MotionEvent event) {
        if (event.getPointerCount() < 2) {
            initialDistance = 0;
        }
        return true;
    }
    
    private void handleDockResize(MotionEvent event) {
        float deltaY = event.getRawY() - lastTouchY;
        lastTouchY = event.getRawY();
        
        ViewGroup.LayoutParams lp = getLayoutParams();
        int newHeight = lp.height;
        
        // Направление изменения размера зависит от позиции закрепления
        if (dockTop) {
            // Если закреплен сверху, увеличиваем размер при движении вниз
            newHeight += (int) deltaY;
        } else {
            // Если закреплен снизу, увеличиваем размер при движении вверх
            newHeight -= (int) deltaY;
        }
        
        // Ограничиваем минимальную и максимальную высоту
        int minHeight = (int) dp(40);
        int maxHeight = ((ViewGroup) getParent()).getHeight() / 2;
        
        newHeight = Math.max(minHeight, Math.min(newHeight, maxHeight));
        
        if (newHeight != lp.height) {
            lp.height = newHeight;
            dockHeightPx = newHeight;
            setLayoutParams(lp);
            
            // Корректируем позицию Y в зависимости от позиции закрепления
            if (dockTop) {
                // Если закреплен сверху, позиция Y всегда должна быть 0
                setY(0);
            } else {
                // Если закреплен снизу, позиция Y должна быть (parentHeight - newHeight)
                float newY = ((ViewGroup) getParent()).getHeight() - newHeight;
                setY(newY);
            }
            
            // Перепозиционируем все docked виджеты после изменения размера
            ViewGroup parent = (ViewGroup) getParent();
            if (parent != null) {
                repositionAllDockedWidgets(parent);
            }
            
            // Уведомляем об изменении размера
            if (dockResizeListener != null) {
                dockResizeListener.onDockResize(newHeight);
            }
        }
    }
    
    private void checkDocking(MotionEvent event) {
        // Проверяем расстояние до краев экрана
        float screenHeight = ((ViewGroup) getParent()).getHeight();
        float y = event.getRawY();
        
        float dockThreshold = dp(100);
        
        if (y < dockThreshold) {
            // Близко к верхнему краю
            targetDragX = 0f;
            targetDragY = 0f;
        } else if (y > screenHeight - dockThreshold) {
            // Близко к нижнему краю
            targetDragX = 0f;
            targetDragY = screenHeight - getHeight();
        } else {
            targetDragX = null;
            targetDragY = null;
        }
    }
    
    private boolean shouldDock(MotionEvent event) {
        return targetDragX != null && targetDragY != null;
    }
    
    private void performDocking(MotionEvent event) {
        float screenHeight = ((ViewGroup) getParent()).getHeight();
        float y = event.getRawY();
        
        boolean dockToTop = y < screenHeight / 2;
        
        // При докинге всегда устанавливаем размер по умолчанию
        dockHeightPx = 0; // Сбрасываем сохраненную высоту
        
        setDocked(true, dockToTop, 0f, dockToTop ? 0f : screenHeight - dp(DEFAULT_DOCK_HEIGHT_DP));
    }
    
    private float getDistance(MotionEvent event) {
        if (event.getPointerCount() < 2) return 0;
        
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(x * x + y * y);
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (isDocked) {
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = dockHeightPx > 0 ? dockHeightPx : (int) dp(DEFAULT_DOCK_HEIGHT_DP);
            setMeasuredDimension(width, height);
        } else {
            int size = (int)(dp(CIRCLE_SIZE_DP) * scaleFactor);
            setMeasuredDimension(size, size);
        }
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        // Вызываем соответствующий метод отрисовки
        if (isDocked) {
            onDrawDock(canvas);
        } else {
            onDrawCircle(canvas);
        }
    }
    
    public void setDocked(boolean docked, boolean top) {
        setDocked(docked, top, getX(), getY());
    }
    
    public void setDocked(boolean docked, boolean top, float targetX, float targetY) {
        if (this.isDocked == docked && this.dockTop == top && getX() == targetX && getY() == targetY) {
            return;
        }
        
        this.dockTop = top;
        
        if (morphAnimator != null && morphAnimator.isRunning()) {
            morphAnimator.cancel();
        }
        
        float startMorph = morphProgress;
        float endMorph = docked ? 0f : 1f;
        
        int startW = getWidth();
        int startH = getHeight();
        
        ViewGroup parent = (ViewGroup) getParent();
        int parentWidth = parent.getWidth();
        int parentHeight = parent.getHeight();
        int dockHeight = (int) dp(DEFAULT_DOCK_HEIGHT_DP);
        int circleSize = (int) dp(CIRCLE_SIZE_DP);
        
        int endW = docked ? parentWidth : circleSize;
        int endH = docked ? dockHeight : circleSize;
        
        float startX = getX();
        float startY = getY();
        float endX = targetX;
        float endY = targetY;
        
        // Если доким, вычисляем правильную позицию с учетом других docked виджетов
        if (docked) {
            endX = 0;
            endY = calculateDockPosition(top);
        } else {
            // При переходе в movable режим сбрасываем размер до дефолтного
            dockHeightPx = 0;
        }
        
        // Сохраняем финальные значения для использования в lambda и inner class
        final float finalStartX = startX;
        final float finalStartY = startY;
        final float finalEndX = endX;
        final float finalEndY = endY;
        
        morphAnimator = ValueAnimator.ofFloat(0f, 1f);
        morphAnimator.setDuration(350);
        morphAnimator.addUpdateListener(anim -> {
            float t = (float) anim.getAnimatedValue();
            morphProgress = startMorph + (endMorph - startMorph) * t;
            
            int w = (int) (startW + (endW - startW) * t);
            int h = (int) (startH + (endH - startH) * t);
            
            ViewGroup.LayoutParams lp = getLayoutParams();
            lp.width = w;
            lp.height = h;
            setLayoutParams(lp);
            
            setX(finalStartX + (finalEndX - finalStartX) * t);
            setY(finalStartY + (finalEndY - finalStartY) * t);
            
            postInvalidateOnAnimation();
        });
        
        morphAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                ViewGroup.LayoutParams lp = getLayoutParams();
                lp.width = endW;
                lp.height = endH;
                setLayoutParams(lp);
                
                setX(finalEndX);
                setY(finalEndY);
                morphProgress = endMorph;
                
                postInvalidateOnAnimation();
                
                isMorphing = false;
            }
        });
        
        morphAnimator.start();
        this.isDocked = docked;
        isMorphing = true;
        
        // Уведомляем об изменении состояния docked
        if (dockStateChangeListener != null) {
            dockStateChangeListener.onDockStateChanged(docked, top);
        }
    }
    
    public boolean isDocked() {
        return isDocked;
    }
    
    public boolean isDockTop() {
        return dockTop;
    }
    
    protected boolean isMorphing() {
        return isMorphing;
    }
    
    public void setOnDockResizeListener(OnDockResizeListener listener) {
        this.dockResizeListener = listener;
    }
    
    public void setOnDockStateChangeListener(OnDockStateChangeListener listener) {
        this.dockStateChangeListener = listener;
    }
    
    protected float dp(float dp) {
        return dp * getResources().getDisplayMetrics().density;
    }
    
    /**
     * Вычисляет правильную позицию для докинга с учетом других docked виджетов
     */
    protected float calculateDockPosition(boolean dockTop) {
        ViewGroup parent = (ViewGroup) getParent();
        if (parent == null) return 0;
        
        int dockHeight = (int) dp(DEFAULT_DOCK_HEIGHT_DP);
        float y = 0;
        
        if (dockTop) {
            // Доким сверху - начинаем с позиции 0
            y = 0;
            
            // Проверяем другие виджеты сверху
            for (int i = 0; i < parent.getChildCount(); i++) {
                View child = parent.getChildAt(i);
                if (child != this && child instanceof BaseDockWidget) {
                    BaseDockWidget otherWidget = (BaseDockWidget) child;
                    if (otherWidget.isDocked() && otherWidget.isDockTop()) {
                        // Если другой виджет уже docked сверху, ставим наш под ним
                        y = otherWidget.getY() + otherWidget.getHeight();
                        break;
                    }
                }
            }
        } else {
            // Доким снизу - начинаем с нижней позиции
            y = parent.getHeight() - dockHeight;
            
            // Проверяем другие виджеты снизу
            for (int i = 0; i < parent.getChildCount(); i++) {
                View child = parent.getChildAt(i);
                if (child != this && child instanceof BaseDockWidget) {
                    BaseDockWidget otherWidget = (BaseDockWidget) child;
                    if (otherWidget.isDocked() && !otherWidget.isDockTop()) {
                        // Если другой виджет уже docked снизу, ставим наш над ним
                        y = otherWidget.getY() - dockHeight;
                        break;
                    }
                }
            }
        }
        
        return y;
    }
    
    /**
     * Перепозиционирует все docked виджеты, чтобы они прижались к краям
     */
    public static void repositionAllDockedWidgets(ViewGroup parent) {
        if (parent == null) return;
        
        // Собираем все docked виджеты сверху
        java.util.List<BaseDockWidget> topWidgets = new java.util.ArrayList<>();
        java.util.List<BaseDockWidget> bottomWidgets = new java.util.ArrayList<>();
        
        for (int i = 0; i < parent.getChildCount(); i++) {
            View child = parent.getChildAt(i);
            if (child instanceof BaseDockWidget) {
                BaseDockWidget widget = (BaseDockWidget) child;
                if (widget.isDocked()) {
                    if (widget.isDockTop()) {
                        topWidgets.add(widget);
                    } else {
                        bottomWidgets.add(widget);
                    }
                }
            }
        }
        
        // Перепозиционируем виджеты сверху
        float currentY = 0;
        for (BaseDockWidget widget : topWidgets) {
            widget.setY(currentY);
            currentY += widget.getHeight();
        }
        
        // Перепозиционируем виджеты снизу
        currentY = parent.getHeight();
        for (int i = bottomWidgets.size() - 1; i >= 0; i--) {
            BaseDockWidget widget = bottomWidgets.get(i);
            currentY -= widget.getHeight();
            widget.setY(currentY);
        }
    }
    
    // Абстрактные методы для переопределения в наследниках
    protected abstract void onDrawDock(Canvas canvas);
    protected abstract void onDrawCircle(Canvas canvas);
}
