#include <X11/Xlib.h>
#include <X11/Xutil.h>

#include <stdexcept>

#include "orientation.hh"

namespace uw {
  Orientation::Orientation(Screen &screen, Image &image)
    : uwScreen{ screen }, uwImage{ image } {
  }

  Orientation::~Orientation() {}

  void Orientation::center() {
    Pixmap pm = XCreatePixmap(uwScreen.getDisplay(), uwScreen.getWindow(),
        uwScreen.getExtent().width, uwScreen.getExtent().height,
        uwImage.getExtent().depth);

    XImage *img = XCreateImage(uwScreen.getDisplay(),
        uwImage.getVisual(), uwImage.getExtent().depth, ZPixmap, 0, nullptr,
        uwImage.getExtent().width, uwImage.getExtent().height, 32, 0);

    if (!img) throw std::runtime_error("中央にする為にXImageの作成に失敗。");

    std::vector<char>
      imgData(uwImage.getExtent().width * uwImage.getExtent().height * 4);
    img->data = imgData.data();

    for (int y = 0; y < uwImage.getExtent().height; y++) {
      for (int x = 0; x < uwImage.getExtent().width; x++) {
        int idx = (y * uwImage.getExtent().width + x) * 4;

        uchar blue  = uwImage.getImageData()[idx + 2];
        uchar green = uwImage.getImageData()[idx + 1];
        uchar red   = uwImage.getImageData()[idx + 0];
        uchar alpha = uwImage.getImageData()[idx + 3];

        ulong pixel = (alpha << 24) | (red << 16) | (green << 8) | blue;
        XPutPixel(img, x, y, pixel);
      }
    }

    XSetForeground(uwScreen.getDisplay(), uwImage.getGC(), 0);
    XFillRectangle(uwScreen.getDisplay(), pm, uwImage.getGC(), 0, 0,
        uwScreen.getExtent().width, uwScreen.getExtent().height);

    int xoff = (uwScreen.getExtent().width - uwImage.getExtent().width) / 2;
    int yoff = (uwScreen.getExtent().height - uwImage.getExtent().height) / 2;
    if (xoff < 0) xoff = 0;
    if (yoff < 0) yoff = 0;

    XPutImage(uwScreen.getDisplay(), pm, uwImage.getGC(), img, 0, 0, xoff, yoff,
        uwImage.getExtent().width, uwImage.getExtent().height);

    XSetWindowBackgroundPixmap(uwScreen.getDisplay(), uwScreen.getWindow(), pm);
    XClearWindow(uwScreen.getDisplay(), uwScreen.getWindow());

    XFreePixmap(uwScreen.getDisplay(), pm);
    img->data = nullptr;
    XDestroyImage(img);
  }

  void Orientation::fill() {
    Pixmap pm = XCreatePixmap(uwScreen.getDisplay(), uwScreen.getWindow(),
        uwScreen.getExtent().width, uwScreen.getExtent().height,
        uwImage.getExtent().depth);

    XImage *img = XCreateImage(uwScreen.getDisplay(),
        uwImage.getVisual(), uwImage.getExtent().depth, ZPixmap, 0, nullptr,
        uwScreen.getExtent().width, uwScreen.getExtent().height, 32, 0);

    if (!img) throw std::runtime_error("スケールする為にXImageの作成に失敗。");

    std::vector<char> sdata(
        uwScreen.getExtent().width * uwScreen.getExtent().height * 4);
    img->data = sdata.data();
    if (!img->data)
      throw std::runtime_error("スケールしたデータにメモリの役割に失敗。");

    double xScale = (double)uwScreen.getExtent().width / uwImage.getExtent().width;
    double yScale = (double)uwScreen.getExtent().height / uwImage.getExtent().height;

    for (int y = 0; y < uwScreen.getExtent().height; y++) {
      for(int x = 0; x < uwScreen.getExtent().width; x++) {
        int srcx = (x / xScale);
        int srcy = (y / yScale);

        if (srcx >= uwImage.getExtent().width) srcx = uwImage.getExtent().width - 1;
        if (srcy >= uwImage.getExtent().height) srcy = uwImage.getExtent().height - 1;

        int sidx = (srcy * uwImage.getExtent().width + srcx) * 4;
        if (sidx >= uwImage.getExtent().width * uwImage.getExtent().height * 4)
          continue;

        uchar blue   = uwImage.getImageData()[sidx + 2];
        uchar green  = uwImage.getImageData()[sidx + 1];
        uchar red    = uwImage.getImageData()[sidx + 0];
        uchar alpha  = uwImage.getImageData()[sidx + 3];

        ulong pixel = (alpha << 24) | (red << 16) | (green << 8) | blue;
        XPutPixel(img, x, y, pixel);
      }
    }

    XClearWindow(uwScreen.getDisplay(), uwScreen.getWindow());
    XPutImage(uwScreen.getDisplay(), pm, uwImage.getGC(), img, 0, 0, 0, 0,
        uwScreen.getExtent().width, uwScreen.getExtent().height);

    XSetWindowBackgroundPixmap(uwScreen.getDisplay(), uwScreen.getWindow(), pm);
    XClearWindow(uwScreen.getDisplay(), uwScreen.getWindow());

    XFreePixmap(uwScreen.getDisplay(), pm);
    img->data = nullptr;
    XDestroyImage(img);
  }

  void Orientation::tile() {
    Pixmap pm = XCreatePixmap(uwScreen.getDisplay(), uwScreen.getWindow(),
        uwScreen.getExtent().width, uwScreen.getExtent().height,
        uwImage.getExtent().depth);

    XImage *img = XCreateImage(uwScreen.getDisplay(),
        uwImage.getVisual(), uwImage.getExtent().depth, ZPixmap, 0, nullptr,
        uwImage.getExtent().width, uwImage.getExtent().height, 32, 0);

    if (!img) throw std::runtime_error("タイルする為にXImageの作成に失敗。");

    std::vector<char>
      imgData(uwImage.getExtent().width * uwImage.getExtent().height * 4);
    img->data = imgData.data();

    for (int y = 0; y < uwImage.getExtent().height; y++) {
      for (int x = 0; x < uwImage.getExtent().width; x++) {
        int idx = (y * uwImage.getExtent().width + x) * 4;

        uchar blue  = uwImage.getImageData()[idx + 2];
        uchar green = uwImage.getImageData()[idx + 1];
        uchar red   = uwImage.getImageData()[idx + 0];
        uchar alpha = uwImage.getImageData()[idx + 3];

        ulong pixel = (alpha << 24) | (red << 16) | (green << 8) | blue;
        XPutPixel(img, x, y, pixel);
      }
    }

    XPutImage(uwScreen.getDisplay(), pm, uwImage.getGC(), img, 0, 0, 0, 0,
        uwImage.getExtent().width, uwImage.getExtent().height);

    XSetWindowBackgroundPixmap(uwScreen.getDisplay(), uwScreen.getWindow(), pm);
    XClearWindow(uwScreen.getDisplay(), uwScreen.getWindow());

    for (int y = 0; y < uwScreen.getExtent().height; y += uwImage.getExtent().height) {
      for (int x = 0; x < uwScreen.getExtent().width; x += uwImage.getExtent().width) {
        XCopyArea(uwScreen.getDisplay(), pm, uwScreen.getWindow(), uwImage.getGC(),
            0, 0, uwImage.getExtent().width, uwImage.getExtent().height, x, y);
      }
    }

    XFreePixmap(uwScreen.getDisplay(), pm);
    img->data = nullptr;
    XDestroyImage(img);
  }
}