#include "DxLib.h"
#include <string>

const int WIDTH = 1200, HEIGHT = 688; // ウィンドウの幅と高さのピクセル数
const int FRAME_RATE = 30; // フレームレートの定義
const int FRAME_TIME = 1000 / FRAME_RATE; // WAIT時間

class UFO { // UFOを動かすクラス
private: // メンバ変数をprivateで宣言する
    int x; // x座標
    int y; // y座標
    int vx; // x軸方向の速さ
    int vy; // y軸方向の速さ

public: // メンバ関数をpublicで宣言する
    void set(int x, int y, int vx, int vy) { // UFOをセットするメンバ関数
        this->x = x; // それぞれのメンバ変数に引数を代入する
        this->y = y;
        this->vx = vx;
        this->vy = vy;
    }
    void move() { // UFOの移動と画面端で跳ね返る処理を行うメンバ関数
        x += vx;
        if (x < 0 || WIDTH < x) vx = -vx; // 画面の左右の端で進む向きを変える
        y += vy;
        if (y < 0 || HEIGHT < y) vy = -vy; // 画面の上下の端で進む向きを変える
    }
    int getX() { return x; } // x座標を返すゲッタ
    int getY() { return y; } // y座標を返すゲッタ
};

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // ＤＸライブラリを使用する準備
    SetWindowText("UFO PANIC"); // ウィンドウのタイトル
    SetGraphMode(WIDTH, HEIGHT, 32); // ウィンドウの大きさとカラービット数の指定
    ChangeWindowMode(TRUE); // ウィンドウモードで起動
    if (DxLib_Init() == -1) return -1; // ライブラリ初期化 エラーが起きたら終了
    SetBackgroundColor(0, 0, 0); // 背景色の指定
    SetDrawScreen(DX_SCREEN_BACK); // 描画面を裏画面にする

    // 画像の読み込み
    int img_bg = LoadGraph("image_Chapter13/ufo_panic/bg.png"); // 変数に背景を読み込む
    int img_ufo = LoadGraph("image_Chapter13/ufo_panic/ufo.png"); // 変数にUFOの画像を読み込む
    int img_copter[10];
    for (int i = 0; i < 10; i++) { // 配列にヘリコプターの画像を読み込む
        std::string file = "image_Chapter13/ufo_panic/copter" + std::to_string(i) + ".png";
        const char* cfile = file.c_str();
        img_copter[i] = LoadGraph(cfile);
    }

    // 定数、変数
    enum { TITLE, PLAY, OVER }; // シーンの定数
    int scene = TITLE; // どのシーンかを管理する変数
    int timer = 0; // 経過時間（フレーム数）を数える変数
    int score = 0; // スコアを代入する変数
    int hisco = 10000; // ハイスコアを代入する変数
    int copter_x = WIDTH / 2;  // ┬ ヘリコプターの座標を代入する変数
    int copter_y = HEIGHT / 2; // ┘
    int copter_dir = 0; // 向きを代入する変数 0が左、2が正面、4が右
    int mouse_x, mouse_y; // マウスポインタの座標を代入する変数
    int dx; // マウスポインタとヘリコプターのx座標の差を代入する変数
    int ufo_no = 0; // UFOをセットする配列の添え字（インデックス）、出現したUFOの数でもある
    bool hit_check; // ヒットチェック用の変数

    // UFO用の定数、オブジェクトの配列
    const int UFO_MAX = 100; // UFOの最大数
    UFO ufo[UFO_MAX]; // UFOのオブジェクト

    while (ProcessMessage() == 0) { // メインループ
        int start_time = GetNowCount(); // 現在時間
        DrawGraph(0, 0, img_bg, FALSE); // 背景の表示
        timer++; // 経過時間（フレーム数）をカウント

        switch (scene) { // シーンごとに処理を分ける
        case TITLE: // ---------- タイトル画面の処理 ----------
            SetFontSize(80);
            DrawString(420, 160, "UFO PANIC", GetColor(255, 0, 224));
            SetFontSize(30);
            if (timer % 30 < 15) DrawString(480, 480, "CLICK TO START.", GetColor(255, 255, 255));
            if (GetMouseInput() & MOUSE_INPUT_LEFT) { // マウスボタンをクリックした時
                scene = PLAY; // ゲームプレイに移行
                timer = 0;
                score = 0;
                copter_x = WIDTH / 2;
                copter_y = HEIGHT / 2;
                ufo[0].set(100, 100, 5, 2); // 一機目のUFOをセット
                ufo_no = 1; // 次にセットする添え字、出現したUFOの数に等しい
            }
            break;

        case PLAY: // ---------- ゲームプレイの処理 ----------
            score += 10; // プレイ中、10点ずつ増える
            // ヘリコプターをマウスで操作する
            GetMousePoint(&mouse_x, &mouse_y); // マウスポインタの座標を取得
            copter_x = copter_x * 0.9 + mouse_x * 0.1; // ┬ 座標を変化させる
            copter_y = copter_y * 0.9 + mouse_y * 0.1; // ┘
            if (copter_x < 0) copter_x = 0;           // ┬ ウィンドウの外に出ないようにする
            if (copter_x > WIDTH) copter_x = WIDTH;   // │
            if (copter_y < 0) copter_y = 0;           // │
            if (copter_y > HEIGHT) copter_y = HEIGHT; // ┘
            dx = copter_x - mouse_x; // ヘリコプターとマウスポインタのx座標の差
            copter_dir = 2; // ヘリコプターの向きをいったん正面にする
            if (dx > 10) copter_dir = 1;  // やや左向き
            if (dx > 50) copter_dir = 0;  // 左向き
            if (dx < -10) copter_dir = 3; // やや右向き
            if (dx < -50) copter_dir = 4; // 右向き
            DrawGraph(copter_x - 48, copter_y - 24, img_copter[copter_dir * 2 + timer % 2], TRUE); // ヘリの表示

            if (timer % 90 == 0 && ufo_no < UFO_MAX) { // UFOを分裂させる
                int x = ufo[0].getX();
                int y = ufo[0].getY();
                int vx = GetRand(10) - 5; // GetRand()はＤＸライブラリの乱数を発生させる関数
                int vy = GetRand(6) - 3;
                ufo[ufo_no].set(x, y, vx, vy); // 新たなUFOのパラメーターをメンバ関数でセット
                ufo_no++; // インデックスを次の番号にする
                score += 1000; // 分裂したタイミングで1000点加算
            }
            hit_check = false; // ヒットチェック用の変数にfalseを代入
            for (int i = 0; i < ufo_no; i++) { // UFOの移動処理、ヒットチェックも行う
                ufo[i].move(); // メンバ関数により座標を変化させる
                DrawGraph(ufo[i].getX() - 32, ufo[i].getY() - 32, img_ufo, TRUE); // UFOの表示
                int dis = (ufo[i].getX() - copter_x) * (ufo[i].getX() - copter_x) + (ufo[i].getY() - copter_y) * (ufo[i].getY() - copter_y); // 二点間の距離を求める（√を使用しないので距離の二乗）
                if (dis < 50 * 50) hit_check = true; // ヘリコプターと接触したか調べる
            }
            if (hit_check == true) { // ヘリコプターがUFOと接触した場合
                scene = OVER; // ゲームオーバーに移行
                timer = 0; // 一定時間経過後にタイトルに戻すため、ここで0を代入
            }
            break;

        case OVER: // ---------- ゲームオーバーの処理 ----------
            SetFontSize(60);
            DrawString(460, 200, "GAME OVER", GetColor(255, 0, 0));
            if (timer > 30 * 5) scene = TITLE; // 一定時間が経過したらタイトルに戻す
            break;
        }

        if (score > hisco) hisco = score; // ハイスコアを更新した時
        SetFontSize(30); // フォントサイズの指定
        DrawFormatString(120, 10, GetColor(128, 255, 128), "SCORE %07d", score); // スコアの表示
        DrawFormatString(880, 10, GetColor(255, 255, 0), "HIGH-SC %07d", hisco); // ハイスコアの表示
        DrawFormatString(120, HEIGHT - 40, GetColor(255, 0, 0), "UFO %d", ufo_no); // UFOの出現数

        ScreenFlip(); // 裏画面の内容を表画面に反映させる
        int end_time = GetNowCount(); // 現在時間
        int elapsed_time = end_time - start_time; // 処理に要した時間
        int wait_time = FRAME_TIME - elapsed_time; // 一時停止する時間
        if (0 < wait_time && wait_time <= FRAME_TIME) WaitTimer(wait_time);
        if (CheckHitKey(KEY_INPUT_ESCAPE) == 1) break; // ESCキーが押されたら終了
    }

    DxLib_End(); // ＤＸライブラリ使用の終了処理
    return 0; // ソフトの終了
}