//ECamera.c

#include "ECamera.h"
#include <math.h> // Для функции fabs()

// Используем double для повышения точности
static double zoom_level = 1.0;
static double zoom_target = 1.0;
static const double zoom_speed = 0.1;       // Скорость зума
static const double zoom_factor = 1.1;      // Коэффициент увеличения зума
static const double max_zoom_level = 20.0;  // Максимальный уровень зума (приближения)
static double min_zoom_level = 1.0;         // Минимальный уровень зума, вычисляется

// Размеры изображения и окна
static int image_width = 0;
static int image_height = 0;
static int window_width = 0;
static int window_height = 0;

// Свойства камеры
static double camera_x = 0.0;
static double camera_y = 0.0;

static int dragging = 0;
static int prev_mouse_x = 0;
static int prev_mouse_y = 0;

// Переменные для зума вокруг курсора
static int is_zooming = 0;
static double cursor_world_x = 0.0;
static double cursor_world_y = 0.0;
static int cursor_screen_x = 0;
static int cursor_screen_y = 0;

// Функция для вычисления минимального уровня зума, чтобы размер камеры не превышал размер изображения
static void calculate_min_zoom_level()
{
    double zoom_w = (double)window_width / (double)image_width;
    double zoom_h = (double)window_height / (double)image_height;

    // Выбираем максимальное значение между отношением размеров окна к изображения по ширине и по высоте
    min_zoom_level = (zoom_w > zoom_h) ? zoom_w : zoom_h;

    // Убедимся, что min_zoom_level не превышает 1.0
    if (min_zoom_level > 1.0)
    {
        min_zoom_level = 1.0;
    }

    // Убедимся, что min_zoom_level не меньше очень малого значения
    if (min_zoom_level < 0.1)
    {
        min_zoom_level = 0.1;
    }
}

// Инициализация настроек зума и камеры
void init_zoom(int img_width, int img_height, int win_width, int win_height)
{
    image_width = img_width;
    image_height = img_height;
    window_width = win_width;
    window_height = win_height;

    calculate_min_zoom_level();

    zoom_level = min_zoom_level;
    zoom_target = min_zoom_level;
    camera_x = 0.0;
    camera_y = 0.0;
}

// Функции зума
void zoom_in(int mouse_x, int mouse_y)
{
    if (zoom_target >= max_zoom_level)
    {
        // Достигнуто максимальное приближение
        return;
    }

    // Сохранение мировых координат под курсором
    cursor_world_x = camera_x + (double)mouse_x / zoom_level;
    cursor_world_y = camera_y + (double)mouse_y / zoom_level;
    cursor_screen_x = mouse_x;
    cursor_screen_y = mouse_y;

    zoom_target *= zoom_factor;

    // Ограничение максимального уровня зума
    if (zoom_target > max_zoom_level)
    {
        zoom_target = max_zoom_level;
    }

    is_zooming = 1;
}

void zoom_out(int mouse_x, int mouse_y)
{
    if (zoom_target <= min_zoom_level)
    {
        // Достигнуто минимальное отдаление
        return;
    }

    // Сохранение мировых координат под курсором
    cursor_world_x = camera_x + (double)mouse_x / zoom_level;
    cursor_world_y = camera_y + (double)mouse_y / zoom_level;
    cursor_screen_x = mouse_x;
    cursor_screen_y = mouse_y;

    zoom_target /= zoom_factor;

    // Ограничение минимального уровня зума
    if (zoom_target < min_zoom_level)
    {
        zoom_target = min_zoom_level;
    }

    is_zooming = 1;
}

void update_zoom()
{
    if (is_zooming)
    {
        // Плавное интерполирование к целевому уровню зума
        double delta = zoom_target - zoom_level;
        double step = delta * zoom_speed;

        if (fabs(delta) < 0.001)
        {
            zoom_level = zoom_target;
            is_zooming = 0;
        }
        else
        {
            zoom_level += step;
        }

        // Корректировка положения камеры, чтобы удерживать мировую позицию курсора под курсором
        camera_x = cursor_world_x - (double)cursor_screen_x / zoom_level;
        camera_y = cursor_world_y - (double)cursor_screen_y / zoom_level;

        // Ограничение позиции камеры после изменения зума
        double view_width = (double)window_width / zoom_level;
        double view_height = (double)window_height / zoom_level;

        double max_camera_x = (double)image_width - view_width;
        double max_camera_y = (double)image_height - view_height;

        if (camera_x < 0.0)
            camera_x = 0.0;
        if (camera_y < 0.0)
            camera_y = 0.0;
        if (camera_x > max_camera_x)
            camera_x = max_camera_x;
        if (camera_y > max_camera_y)
            camera_y = max_camera_y;
    }
}

// Функции перетаскивания камеры
void start_camera_drag(int mouse_x, int mouse_y)
{
    dragging = 1;
    prev_mouse_x = mouse_x;
    prev_mouse_y = mouse_y;
}

void stop_camera_drag()
{
    dragging = 0;
}

void update_camera_drag(int mouse_x, int mouse_y)
{
    if (dragging)
    {
        // Вычисление разницы в движении мыши
        int delta_x = mouse_x - prev_mouse_x;
        int delta_y = mouse_y - prev_mouse_y;

        // Преобразование движения мыши в мировые координаты
        double world_delta_x = delta_x / zoom_level;
        double world_delta_y = delta_y / zoom_level;

        // Обновление положения камеры
        camera_x -= world_delta_x;
        camera_y -= world_delta_y;

        // Ограничение позиции камеры
        double view_width = (double)window_width / zoom_level;
        double view_height = (double)window_height / zoom_level;

        double max_camera_x = (double)image_width - view_width;
        double max_camera_y = (double)image_height - view_height;

        if (camera_x < 0.0)
            camera_x = 0.0;
        if (camera_y < 0.0)
            camera_y = 0.0;
        if (camera_x > max_camera_x)
            camera_x = max_camera_x;
        if (camera_y > max_camera_y)
            camera_y = max_camera_y;

        // Обновление предыдущего положения мыши
        prev_mouse_x = mouse_x;
        prev_mouse_y = mouse_y;
    }
}

// Обработка изменения размера окна
void set_window_size(int win_width, int win_height)
{
    window_width = win_width;
    window_height = win_height;

    calculate_min_zoom_level();

    // Убедимся, что текущий уровень зума не меньше минимального
    if (zoom_level < min_zoom_level)
    {
        zoom_level = min_zoom_level;
        zoom_target = min_zoom_level;
    }

    // Ограничение позиции камеры
    double view_width = (double)window_width / zoom_level;
    double view_height = (double)window_height / zoom_level;

    double max_camera_x = (double)image_width - view_width;
    double max_camera_y = (double)image_height - view_height;

    if (camera_x < 0.0)
        camera_x = 0.0;
    if (camera_y < 0.0)
        camera_y = 0.0;
    if (camera_x > max_camera_x)
        camera_x = max_camera_x;
    if (camera_y > max_camera_y)
        camera_y = max_camera_y;
}

// Геттеры
double get_zoom_level()
{
    return zoom_level;
}

SDL_FRect get_destination_rect()
{
    SDL_FRect dst_rect;

    // Рассчитываем позицию и размер текстуры с учетом камеры и зума
    dst_rect.x = -(float)(camera_x * zoom_level);
    dst_rect.y = -(float)(camera_y * zoom_level);
    dst_rect.w = (float)(image_width * zoom_level);
    dst_rect.h = (float)(image_height * zoom_level);

    return dst_rect;
}

// Добавляем геттеры для позиции камеры
double get_camera_x()
{
    return camera_x;
}

double get_camera_y()
{
    return camera_y;
}
//ECamera.h

#ifndef ECAMERA_H
#define ECAMERA_H

#include <SDL2/SDL.h>

// Инициализация настроек зума и камеры
void init_zoom(int image_width, int image_height, int window_width, int window_height);

// Контроль зума
void zoom_in(int mouse_x, int mouse_y);
void zoom_out(int mouse_x, int mouse_y);
void update_zoom();

// Контроль камеры
void start_camera_drag(int mouse_x, int mouse_y);
void stop_camera_drag();
void update_camera_drag(int mouse_x, int mouse_y);

// Обработка изменения размера окна
void set_window_size(int window_width, int window_height);

// Геттеры
double get_zoom_level();
SDL_FRect get_destination_rect();
double get_camera_x();
double get_camera_y();

#endif // ECAMERA_H
//EditorProvince.c

#include "EditorProvince.h"
#include "ECamera.h"
#include <SDL2/SDL2_gfxPrimitives.h> // For drawing primitives
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#define MAX_PROVINCES 1000          // Maximum number of provinces
#define MAX_POLYGON_POINTS 4000     // Maximum number of points in a polygon

typedef struct {
    int numPoints;
    int x[MAX_POLYGON_POINTS];
    int y[MAX_POLYGON_POINTS];
    Uint8 colorR, colorG, colorB, colorA;  // Color of the province
} Province;

// Array to store all the provinces
static Province provinces[MAX_PROVINCES];
static int numProvinces = 0;

// Current polygon data (world coordinates)
static int currentNumPoints = 0;
static int currentPolygonX[MAX_POLYGON_POINTS];
static int currentPolygonY[MAX_POLYGON_POINTS];
static Uint8 currentColorR, currentColorG, currentColorB, currentColorA;

// Province data
static int provinceNumber;

// Function to generate a random color
static void GenerateRandomColor(Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a)
{
    *r = rand() % 256;
    *g = rand() % 256;
    *b = rand() % 256;
    *a = 128; // Semi-transparent
}

// Function to save the polygon to a file
static void SavePolygonToFile()
{
    char filename[256];
    snprintf(filename, sizeof(filename), "%d.txt", provinceNumber);

    FILE *file = fopen(filename, "w");
    if (!file)
    {
        printf("Failed to open file %s for writing\n", filename);
        return;
    }

    for (int i = 0; i < currentNumPoints; ++i)
    {
        fprintf(file, "%d %d\n", currentPolygonX[i], currentPolygonY[i]);
    }
    fclose(file);

    printf("Province %d saved to %s\n", provinceNumber, filename);
}

// Initialize the province editor
void EditorProvince_Init()
{
    currentNumPoints = 0; // Start without points
    provinceNumber = 1;
    numProvinces = 0;

    // Seed random number generator
    srand((unsigned int)time(NULL));

    // Generate first random color
    GenerateRandomColor(&currentColorR, &currentColorG, &currentColorB, &currentColorA);
}

// Clean up resources of the province editor
void EditorProvince_Cleanup()
{
    // No dynamic resources to clean up
}

// Handle mouse down event to add a point to the current polygon
void EditorProvince_HandleMouseDown(int x, int y)
{
    // Convert screen coordinates to world coordinates
    double zoomLevel = get_zoom_level();
    double cameraX = get_camera_x();
    double cameraY = get_camera_y();

    // Convert and round to nearest whole pixel
    int worldX = (int)(cameraX + x / zoomLevel + 0.5);
    int worldY = (int)(cameraY + y / zoomLevel + 0.5);

    if (currentNumPoints < MAX_POLYGON_POINTS)
    {
        currentPolygonX[currentNumPoints] = worldX;
        currentPolygonY[currentNumPoints] = worldY;
        currentNumPoints++;
    }
}

// Handle key down events
void EditorProvince_HandleKeyDown(SDL_Keycode key)
{
    if (key == SDLK_BACKSPACE)
    {
        if (currentNumPoints > 0)
        {
            currentNumPoints--; // Remove the last point
        }
    }
    else if (key == SDLK_RETURN)
    {
        if (currentNumPoints >= 3)
        {
            // Save current polygon to file
            SavePolygonToFile();

            if (numProvinces < MAX_PROVINCES)
            {
                // Save current polygon to provinces array
                Province *prov = &provinces[numProvinces];
                prov->numPoints = currentNumPoints;
                for (int i = 0; i < currentNumPoints; ++i)
                {
                    prov->x[i] = currentPolygonX[i];
                    prov->y[i] = currentPolygonY[i];
                }
                prov->colorR = currentColorR;
                prov->colorG = currentColorG;
                prov->colorB = currentColorB;
                prov->colorA = currentColorA;

                numProvinces++;
            }
            else
            {
                printf("Maximum number of provinces reached!\n");
            }

            // Reset current polygon for new province
            currentNumPoints = 0;
            provinceNumber++;     // Increment province number
            // Generate new random color for next province
            GenerateRandomColor(&currentColorR, &currentColorG, &currentColorB, &currentColorA);
        }
    }
}

// Draw the provinces and current polygon
void EditorProvince_Draw(SDL_Renderer *renderer)
{
    double zoomLevel = get_zoom_level();
    double cameraX = get_camera_x();
    double cameraY = get_camera_y();

    // First draw all saved provinces
    for (int p = 0; p < numProvinces; ++p)
    {
        Province *prov = &provinces[p];
        if (prov->numPoints >= 3)
        {
            // Arrays for screen coordinates
            Sint16 vx[MAX_POLYGON_POINTS];
            Sint16 vy[MAX_POLYGON_POINTS];

            for (int i = 0; i < prov->numPoints; ++i)
            {
                double screenX = (prov->x[i] - cameraX) * zoomLevel;
                double screenY = (prov->y[i] - cameraY) * zoomLevel;

                vx[i] = (Sint16)screenX;
                vy[i] = (Sint16)screenY;
            }

            // Draw filled polygon with its color
            filledPolygonRGBA(renderer, vx, vy, prov->numPoints,
                              prov->colorR, prov->colorG, prov->colorB, prov->colorA);
        }
    }

    // Then draw the current polygon being drawn
    if (currentNumPoints >= 3)
    {
        // Arrays for screen coordinates
        Sint16 vx[MAX_POLYGON_POINTS];
        Sint16 vy[MAX_POLYGON_POINTS];

        for (int i = 0; i < currentNumPoints; ++i)
        {
            double screenX = (currentPolygonX[i] - cameraX) * zoomLevel;
            double screenY = (currentPolygonY[i] - cameraY) * zoomLevel;

            vx[i] = (Sint16)screenX;
            vy[i] = (Sint16)screenY;
        }

        // Draw filled polygon with current color
        filledPolygonRGBA(renderer, vx, vy, currentNumPoints,
                          currentColorR, currentColorG, currentColorB, currentColorA);
    }

    // Draw points of the current polygon as small filled circles
    for (int i = 0; i < currentNumPoints; ++i)
    {
        double screenX = (currentPolygonX[i] - cameraX) * zoomLevel;
        double screenY = (currentPolygonY[i] - cameraY) * zoomLevel;

        filledCircleRGBA(renderer, (Sint16)screenX, (Sint16)screenY, 3,
                         0, 255, 0, 255); // Green color for points
    }
}
//EditorProvince.h

#ifndef EDITOR_PROVINCE_H
#define EDITOR_PROVINCE_H

#include <SDL2/SDL.h>

// Initialize the province editor
void EditorProvince_Init();

// Clean up resources of the province editor
void EditorProvince_Cleanup();

// Handle mouse down event to add a point
void EditorProvince_HandleMouseDown(int x, int y);

// Handle key down events
void EditorProvince_HandleKeyDown(SDL_Keycode key);

// Draw the polygons and points
void EditorProvince_Draw(SDL_Renderer *renderer);

#endif // EDITOR_PROVINCE_H
//MapEditor.c

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <stdio.h>
#include "ECamera.h"
#include "EditorProvince.h"

#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600

int main()
{
  SDL_Window *window = NULL;
  SDL_Renderer *renderer = NULL;
  SDL_Texture *texture = NULL;
  SDL_Event event;
  int quit = 0;

  int image_width, image_height;

  // Initialize SDL
  if (SDL_Init(SDL_INIT_VIDEO) < 0)
    {
      printf("Failed to initialize SDL! SDL Error: %s\n", SDL_GetError());
      return -1;
    }

  // Initialize SDL_image
  if (!(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG))
    {
      printf("Failed to initialize SDL_image! SDL_image Error: %s\n", IMG_GetError());
      SDL_Quit();
      return -1;
    }

  // Create window
  window = SDL_CreateWindow("OpenAge Map Editor",
			    SDL_WINDOWPOS_CENTERED,
			    SDL_WINDOWPOS_CENTERED,
			    WINDOW_WIDTH,
			    WINDOW_HEIGHT,
			    SDL_WINDOW_RESIZABLE);

  if (window == NULL)
    {
      printf("Failed to create window! SDL Error: %s\n", SDL_GetError());
      IMG_Quit();
      SDL_Quit();
      return -1;
    }

  // Create renderer
  renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
  if (renderer == NULL)
    {
      printf("Failed to create renderer! SDL Error: %s\n", SDL_GetError());
      SDL_DestroyWindow(window);
      IMG_Quit();
      SDL_Quit();
      return -1;
    }

  // Load image
  SDL_Surface *loadedSurface = IMG_Load("./map/world/background.png");
  if (loadedSurface == NULL)
    {
      printf("Failed to load image %s! SDL_image Error: %s\n", "./map/world/background.png", IMG_GetError());
      SDL_DestroyRenderer(renderer);
      SDL_DestroyWindow(window);
      IMG_Quit();
      SDL_Quit();
      return -1;
    }

  // Create texture from surface
  texture = SDL_CreateTextureFromSurface(renderer, loadedSurface);
  if (texture == NULL)
    {
      printf("Failed to create texture from %s! SDL Error: %s\n", "./map/world/background.png", SDL_GetError());
      SDL_FreeSurface(loadedSurface);
      SDL_DestroyRenderer(renderer);
      SDL_DestroyWindow(window);
      IMG_Quit();
      SDL_Quit();
      return -1;
    }

  image_width = loadedSurface->w;
  image_height = loadedSurface->h;

  SDL_FreeSurface(loadedSurface);

  // Get initial window size
  int window_width, window_height;
  SDL_GetWindowSize(window, &window_width, &window_height);

  // Initialize zoom and camera
  init_zoom(image_width, image_height, window_width, window_height);

  // Initialize province editor
  EditorProvince_Init();

  // Main loop
  while (!quit)
    {
      // Event handling
      while (SDL_PollEvent(&event))
        {
	  switch (event.type)
            {
	    case SDL_QUIT:
	      quit = 1;
	      break;

	    case SDL_WINDOWEVENT:
	      if (event.window.event == SDL_WINDOWEVENT_RESIZED)
		{
		  // Update window size
		  SDL_GetWindowSize(window, &window_width, &window_height);
		  set_window_size(window_width, window_height);
		}
	      break;

	    case SDL_MOUSEBUTTONDOWN:
	      if (event.button.button == SDL_BUTTON_LEFT)
		{
		  // Start dragging
		  start_camera_drag(event.button.x, event.button.y);
		}
	      else if (event.button.button == SDL_BUTTON_RIGHT)
		{
		  // Add point to polygon
		  EditorProvince_HandleMouseDown(event.button.x, event.button.y);
		}
	      break;

	    case SDL_MOUSEBUTTONUP:
	      if (event.button.button == SDL_BUTTON_LEFT)
		{
		  // Stop dragging
		  stop_camera_drag();
		}
	      break;

	    case SDL_MOUSEMOTION:
	      // Update camera dragging
	      update_camera_drag(event.motion.x, event.motion.y);
	      break;

	    case SDL_MOUSEWHEEL:
	      {
		// Get mouse position
		int mouse_x, mouse_y;
		SDL_GetMouseState(&mouse_x, &mouse_y);
		// Zoom in and out
		if (event.wheel.y > 0)
		  {
		    zoom_in(mouse_x, mouse_y);
		  }
		else if (event.wheel.y < 0)
		  {
		    zoom_out(mouse_x, mouse_y);
		  }
		break;
	      }
	    case SDL_KEYDOWN:
	      // Обработка нажатия клавиши
	      EditorProvince_HandleKeyDown(event.key.keysym.sym);
	      break;

	    default:
	      break;
            }
        }

      // Update zoom level
      update_zoom();

      // Get the destination rectangle for rendering
      SDL_FRect dst_rect = get_destination_rect();

      // Clear renderer
      SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // Black background
      SDL_RenderClear(renderer);

      // Render the texture using floating point coordinates
      SDL_RenderCopyF(renderer, texture, NULL, &dst_rect);

      // Draw the polygons and points
      EditorProvince_Draw(renderer);

      // Update screen
      SDL_RenderPresent(renderer);

      // Delay to control frame rate
      SDL_Delay(16); // Approximately 60 frames per second
    }

  // Clean up
  EditorProvince_Cleanup();
  SDL_DestroyTexture(texture);
  SDL_DestroyRenderer(renderer);
  SDL_DestroyWindow(window);
  IMG_Quit();
  SDL_Quit();

  return 0;
}

Создай код EditorUI.c и .h, который отображает картинку ui/screen_shadow.png поверх окна редактора. В центре отображай изображение ui/input_panel1.png, в которую можно вводить текст с шрифтом ui/fonts/NotoSansMono-Bold.ttf. Область ввода будет высотой одну строку и будет на 20 пикселей короче с лева и с права изображения input_panel1.png. Справа от поля ввода будет распологаться кнопка с изображением accept_sign.png, а слева от поля ввода кнопка с изображением decline_sign.png. Введенный текст должен быть длинной максимум как выделенная область ввода, и у него должна быть способность стирания на backspace. Если в это поле пользователь ввел текст и нажал кнопку accept_sign, то в файле config.txt(если его нет, то он создается) будет создана строка EDITINGMAP=<введеный текст из поля ввода>. Это должна быть ссылка на папку, если это будет не существующая папка, то на 20 пикселей снизу от ui/input_panel1.png будет появляться ошибкаи медленно пропадать через 5 секунд. Папка из поля ввода - это место,куда сохраняются координаты точек полигонов провинций. После нажатия кнопки accept_sign все элементы интерфейса пропадают и появляется возможность управлять камерой и создавать полигоны провинций. Кнопка decline_sign должна стирать весь текст в поле ввода.