package de.siphalor.was.visual;

import de.siphalor.was.WhatAStorage;
import de.siphalor.was.assets.AssetsManager;
import de.siphalor.was.content.product.Product;
import de.siphalor.was.content.quest.Quest;
import de.siphalor.was.game.Transaction;
import de.siphalor.was.visual.canvas.layout.FixedAspectLayout;
import de.siphalor.was.visual.canvas.layout.FulfillingLayout;
import org.jetbrains.annotations.NotNull;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferStrategy;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class CanvasVisual implements Visual {
	@NotNull
	public static final Image MISSINGNO = getImage("textures/missingno.png");
	private static final double ASPECT_RATIO = 16.0 / 9.0;
	private static final Map<String, Image> imageCache = new HashMap<>();
	private WhatAStorage main;

	private final Frame frame = new Frame(WhatAStorage.TITLE);
	private final Canvas canvas = new Canvas();
	private BufferStrategy bufferStrategy;
	private long minTickTime = 1000L / 60L;

	private boolean fullScreen = false;

	@NotNull
	public static Image getImage(@NotNull String path) {
		Image image = imageCache.get(path);
		if (image == null) {
			image = AssetsManager.getStream(path).map(inputStream -> {
				try {
					return (Image) ImageIO.read(inputStream);
				} catch (IOException e) {
					e.printStackTrace();
				}
				return null;
			}).orElse(MISSINGNO);
			imageCache.put(path, image);
		}
		return image;
	}

	@Override
	public void setup(WhatAStorage whatAStorage) {
		main = whatAStorage;
		frame.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				super.windowClosing(e);
				main.scheduleStop();
			}
		});
		frame.addComponentListener(new ComponentAdapter() {
			@Override
			public void componentResized(ComponentEvent e) {
				super.componentResized(e);
			}
		});
		frame.addKeyListener(new KeyListener() {
			@Override
			public void keyTyped(KeyEvent e) {

			}

			@Override
			public void keyPressed(KeyEvent e) {
				if (e.getExtendedKeyCode() == KeyEvent.VK_F11) {
					GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0];
					frame.dispose();
					if (fullScreen) {
						System.out.println("End fullscreen");
						frame.setUndecorated(false);
						frame.setVisible(true);
						device.setFullScreenWindow(null);
						fullScreen = false;
					} else {
						System.out.println("Start fullscreen");
						frame.setUndecorated(true);
						frame.setVisible(true);
						device.setFullScreenWindow(frame);
						fullScreen = true;
					}
				}
			}

			@Override
			public void keyReleased(KeyEvent e) {

			}
		});

		Panel panel = new Panel();
		panel.setBackground(Color.BLACK);
		frame.add(panel);
		frame.setLayout(new FulfillingLayout(panel));

		panel.add(canvas);
		panel.setLayout(new FixedAspectLayout(ASPECT_RATIO, canvas));

		frame.setVisible(true);
		frame.pack();
		frame.setLocationRelativeTo(null);

		canvas.createBufferStrategy(2);
		bufferStrategy = canvas.getBufferStrategy();
	}

	@Override
	public void run() {
		long time, timeDelta, tickBegin = System.nanoTime();

		while (!main.isStopScheduled()) {
			time = System.nanoTime();
			timeDelta = time - tickBegin;
			tickBegin = System.nanoTime();

			do {
				do {
					Graphics graphics = bufferStrategy.getDrawGraphics();

					graphics.dispose();

				} while(bufferStrategy.contentsRestored());

				bufferStrategy.show();
			} while(bufferStrategy.contentsLost());

			time = System.nanoTime();
			timeDelta = time - tickBegin;

			if (timeDelta < minTickTime) {
				try {
					Thread.sleep((minTickTime - timeDelta) / 1_000_000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

		frame.setVisible(false);
		frame.dispose();
	}

	@Override
	public void onScheduleStop() {

	}

	@Override
	public void invalidateI18n() {

	}

	@Override
	public void onBalanceChanged(int budget, Transaction transaction, int totalIncome, int totalLoss) {

	}

	@Override
	public void onQuestAdded(Quest newQuest, boolean canCreateMore) {

	}

	@Override
	public void onQuestRemoved(int index) {

	}

	@Override
	public void onProductSet(int x, int y, int z, Product product) {

	}

	@Override
	public void onProductCleared(int x, int y, int z, Product product) {

	}
}
