<repo_name>dcfreema/CSE337project<file_sep>/src/main.go
package main

import (
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	_ "github.com/lib/pq"
)

var templates *template.Template
var databaseConnection DatabaseConnection
var database *sql.DB

type ErrorMsg struct {
	Error string
}

type State struct {
	Url    string
	Action string
}

type User struct {
	Username string
	Password string
}

type DatabaseConnection struct {
	Host     string
	Port     int64
	Username string
	Password string
	Dbname   string
}

type Forum struct {
	Id          int64
	ForumPostId int64
	Name        string
	Description string
	Posts       []ForumPost
}

type ForumPost struct {
	ForumId   int64
	Id        int64
	Name      string
	Author    string
	Timestamp string
	Threads   []ForumPostThread
}

type ForumPostThread struct {
	FormPostId int64
	Id         int64
	Body       string
	Author     string
	Timestamp  string
}

type GetForumRequest struct {
	ForumId     int64
	ForumPostId int64
}

func getDatabaseConnectionInfo(filename string) (DatabaseConnection, error) {
	file, err := os.Open(filename)
	if err != nil {
		fmt.Println(err.Error())
		return DatabaseConnection{}, err
	}

	var config DatabaseConnection
	decoder := json.NewDecoder(file)
	err = decoder.Decode(&config)
	if err != nil {
		fmt.Println(err.Error())
		return DatabaseConnection{}, err
	}

	return config, nil
}

func init() {
	t, err := template.ParseFiles(
		"tmpl/header.tmpl",
		"tmpl/index.tmpl",
		"tmpl/footer.tmpl",
		"tmpl/forumlist.tmpl",
		"tmpl/forumposts.tmpl",
		"tmpl/forumpostthreads.tmpl",
		"tmpl/newpost.tmpl",
		"tmpl/signup.tmpl",
		"tmpl/congrats.tmpl",
		"tmpl/login.tmpl",
		"tmpl/game.tmpl",
	)

	templates = template.Must(t, err)

	databaseConnection, err = getDatabaseConnectionInfo("datasource.json")
	if err != nil {
		panic("Cannot connect to the database")
	}

	dbUrl := fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=disable", databaseConnection.Username, databaseConnection.Password, databaseConnection.Host, databaseConnection.Dbname)
	database, err = sql.Open("postgres", dbUrl)
	if err != nil {
		panic(err)
	}
}

func forum(w http.ResponseWriter, r *http.Request) {
	var state State
	_, err := r.Cookie("username")
	if err != nil {
		state.Url = "/login"
		state.Action = "Login"
	} else {
		state.Url = "/logout"
		state.Action = "Logout"
	}

	urlParts := strings.Split(r.URL.Path, "/")
	fmt.Println(urlParts)
	var request GetForumRequest

	if len(urlParts) > 3 {
		forumId, _ := strconv.ParseInt(urlParts[2], 10, 64)
		request.ForumId = forumId
	}

	if len(urlParts) >= 4 {
		forumPostId, _ := strconv.ParseInt(urlParts[3], 10, 64)
		request.ForumPostId = forumPostId
	}

	fmt.Println(request)

	//Display all the forums to the user
	if request.ForumId <= 0 {
		var forums []Forum
		rows, err := database.Query("SELECT id, name, description from forum")
		if err != nil {
			fmt.Println(err)
			return
		}
		defer rows.Close()

		for rows.Next() {
			var forum Forum
			if err := rows.Scan(&forum.Id, &forum.Name, &forum.Description); err != nil {
				fmt.Println(err)
			} else {
				forums = append(forums, forum)
			}
		}

		fmt.Println(forums)
		templates.ExecuteTemplate(w, "header.tmpl", state)
		templates.ExecuteTemplate(w, "forumlist.tmpl", &forums)
		templates.ExecuteTemplate(w, "footer.tmpl", nil)
		return
	}

	//Display all the posts of the forum to the user
	if request.ForumId >= 1 && request.ForumPostId <= 0 {
		var forum Forum
		err := database.QueryRow("select name from forum where id = $1", request.ForumId).Scan(&forum.Name)
		if err != nil {
			fmt.Println(err)
			return
		}
		forum.Id = request.ForumId

		var forumPosts []ForumPost
		rows, err := database.Query("SELECT id, forumid, name, author, timestamp from forumpost where forumid = $1", request.ForumId)
		if err != nil {
			fmt.Println(err)
			return
		}
		defer rows.Close()

		for rows.Next() {
			var forumPost ForumPost
			if err := rows.Scan(&forumPost.Id, &forumPost.ForumId, &forumPost.Name, &forumPost.Author, &forumPost.Timestamp); err != nil {
				fmt.Println(err)
			} else {
				forumPosts = append(forumPosts, forumPost)
			}
		}

		fmt.Println(forumPosts)
		forum.Posts = forumPosts
		templates.ExecuteTemplate(w, "header.tmpl", state)
		templates.ExecuteTemplate(w, "forumposts.tmpl", &forum)
		templates.ExecuteTemplate(w, "footer.tmpl", nil)
		return
	}

	//Display all the threads of the post of the forum to the use
	if request.ForumId >= 1 && request.ForumPostId >= 1 {
		var forum Forum
		forum.Id = request.ForumId
		forum.ForumPostId = request.ForumPostId
		err := database.QueryRow("select name from forum where id = $1", request.ForumId).Scan(&forum.Name)
		if err != nil {
			fmt.Println(err)
			return
		}

		var forumPost ForumPost
		err = database.QueryRow("SELECT name from forumpost where id = $1", request.ForumPostId).Scan(&forumPost.Name)
		if err != nil {
			fmt.Println(err)
			return
		}

		var forumPostThreads []ForumPostThread
		rows, err := database.Query("SELECT id, body, author, timestamp from forumpostthread where forumpostid = $1", request.ForumPostId)
		if err != nil {
			fmt.Println(err)
			return
		}
		defer rows.Close()

		for rows.Next() {
			var forumPostThread ForumPostThread
			if err := rows.Scan(&forumPostThread.Id, &forumPostThread.Body, &forumPostThread.Author, &forumPostThread.Timestamp); err != nil {
				fmt.Println(err)
			} else {
				forumPostThreads = append(forumPostThreads, forumPostThread)
			}
		}

		forumPost.Threads = forumPostThreads
		forum.Posts = append(forum.Posts, forumPost)
		fmt.Println(forum)
		templates.ExecuteTemplate(w, "header.tmpl", state)
		templates.ExecuteTemplate(w, "forumpostthreads.tmpl", &forum)
		templates.ExecuteTemplate(w, "footer.tmpl", nil)
		return
	}
}

func newpost(w http.ResponseWriter, r *http.Request) {
	var state State
	_, err := r.Cookie("username")
	if err != nil {
		state.Url = "/login"
		state.Action = "Login"
	} else {
		state.Url = "/logout"
		state.Action = "Logout"
	}

	urlParts := strings.Split(r.URL.Path, "/")
	fmt.Println(urlParts)
	var request GetForumRequest

	if len(urlParts) > 3 {
		forumId, _ := strconv.ParseInt(urlParts[2], 10, 64)
		request.ForumId = forumId
	}

	if len(urlParts) >= 4 {
		forumPostId, _ := strconv.ParseInt(urlParts[3], 10, 64)
		request.ForumPostId = forumPostId
	}

	fmt.Println(request)
	templates.ExecuteTemplate(w, "header.tmpl", state)
	templates.ExecuteTemplate(w, "newpost.tmpl", &request)
	templates.ExecuteTemplate(w, "footer.tmpl", nil)
	return
}

func insertNewPost(forumId int64, author, name string) int64 {
	var id int64
	t := time.Now()
	date := fmt.Sprintf("%d-%02d-%02d\n", t.Year(), t.Month(), t.Day())

	err := database.QueryRow(`insert into forumpost (forumid, name, author, timestamp) values($1, $2, $3, $4) returning id`, forumId, name, author, date).Scan(&id)

	fmt.Println(err)
	fmt.Println(id)
	return id
}

func insertNewPostThread(id int64, body, author, timestamp string) {
	t := time.Now()
	date := fmt.Sprintf("%d-%02d-%02d\n", t.Year(), t.Month(), t.Day())

	err := database.QueryRow(`insert into forumpostthread (forumpostid, body, author, timestamp) values($1, $2, $3, $4) returning id`, id, body, author, date).Scan(&id)
	if err != nil {
		fmt.Println(err)
	}
}

func post(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Invalid Request!", http.StatusMethodNotAllowed)
		return
	}

	var state State
	cookie, err := r.Cookie("username")
	if err != nil {
		state.Url = "/login"
		state.Action = "Login"
	} else {
		state.Url = "/logout"
		state.Action = "Logout"
	}

	if err != nil {
		//user not logged in
		http.Redirect(w, r, "/login", http.StatusFound)
		return
	}

	if cookie.Value == "" {
		//user not logged in
		http.Redirect(w, r, "/login", http.StatusFound)
		return
	}

	username := cookie.Value

	r.ParseForm()
	fmt.Println(r.Form)

	forumId, _ := strconv.ParseInt(r.PostFormValue("forum"), 10, 64)
	forumPostId, _ := strconv.ParseInt(r.PostFormValue("forumpost"), 10, 64)

	body := r.PostFormValue("body")
	//If it is a new post to the forum
	if forumId > 0 && forumPostId == 0 {
		name := r.PostFormValue("name")

		id := insertNewPost(forumId, username, name)
		insertNewPostThread(id, body, username, "2015-10-28")
		fmt.Println("New Thread Created")
	}

	//If it is a new reply to the forum post
	if forumId > 0 && forumPostId > 0 {
		insertNewPostThread(forumPostId, body, username, "2015-10-28")
		fmt.Println("New Post added")
	}

	url := fmt.Sprintf("http://localhost:8080/forum/%d/%d", forumId, forumPostId)
	http.Redirect(w, r, url, http.StatusFound)
}

func signup(w http.ResponseWriter, r *http.Request) {
	var state State
	_, err := r.Cookie("username")
	if err != nil {
		state.Url = "/login"
		state.Action = "Login"
	} else {
		state.Url = "/logout"
		state.Action = "Logout"
	}

	if r.Method == "POST" {
		r.ParseForm()
		username := r.PostFormValue("username")
		password := r.PostFormValue("<PASSWORD>")
		verify := r.PostFormValue("verify")

		user := User{username, password}

		if password != verify {
			data := ErrorMsg{"passwords do not match"}

			templates.ExecuteTemplate(w, "header.tmpl", state)
			templates.ExecuteTemplate(w, "signup.tmpl", data)
			templates.ExecuteTemplate(w, "footer.tmpl", nil)
			return
		}

		rows, err := database.Query("SELECT username from users where username=$1", username)
		if err != nil {
			fmt.Println(err)
			return
		}
		defer rows.Close()

		for rows.Next() {
			data := ErrorMsg{"username already taken"}

			templates.ExecuteTemplate(w, "header.tmpl", state)
			templates.ExecuteTemplate(w, "signup.tmpl", data)
			templates.ExecuteTemplate(w, "footer.tmpl", nil)
			return
		}

		var id int
		err = database.QueryRow(`insert into users (username, password) values($1, $2) returning id`, username, password).Scan(&id)
		if err != nil {
			fmt.Println(err)
		}

		err = database.QueryRow(`insert into savedata (username, data) values($1, $2) returning id`, username, "").Scan(&id)
		if err != nil {
			fmt.Println(err)
		}

		templates.ExecuteTemplate(w, "header.tmpl", state)
		templates.ExecuteTemplate(w, "congrats.tmpl", user)
		templates.ExecuteTemplate(w, "footer.tmpl", nil)
		return
	}

	templates.ExecuteTemplate(w, "header.tmpl", state)
	templates.ExecuteTemplate(w, "signup.tmpl", nil)
	templates.ExecuteTemplate(w, "footer.tmpl", nil)
}

func login(w http.ResponseWriter, r *http.Request) {
	var state State
	_, err := r.Cookie("username")
	if err != nil {
		state.Url = "/login"
		state.Action = "Login"
	} else {
		state.Url = "/logout"
		state.Action = "Logout"
	}

	if r.Method == "POST" {
		r.ParseForm()
		username := r.PostFormValue("username")
		password := r.PostFormValue("<PASSWORD>")

		rows, err := database.Query("SELECT username from users where username=$1 and password=$2", username, password)
		if err != nil {
			fmt.Println(err)
			return
		}
		defer rows.Close()

		for rows.Next() {
			//user logged in
			loginCookie := &http.Cookie{Name: "username", Value: username, Expires: time.Now().Add(time.Hour), Path: "/"}
			http.SetCookie(w, loginCookie)
			http.Redirect(w, r, "/game", http.StatusFound)
			return
		}

		//not logged in
		data := ErrorMsg{"invalid username and or password"}

		templates.ExecuteTemplate(w, "header.tmpl", state)
		templates.ExecuteTemplate(w, "login.tmpl", data)
		templates.ExecuteTemplate(w, "footer.tmpl", nil)
		return
	}

	templates.ExecuteTemplate(w, "header.tmpl", state)
	templates.ExecuteTemplate(w, "login.tmpl", nil)
	templates.ExecuteTemplate(w, "footer.tmpl", nil)
}

func game(w http.ResponseWriter, r *http.Request) {
	var state State
	_, err := r.Cookie("username")
	if err != nil {
		state.Url = "/login"
		state.Action = "Login"
	} else {
		state.Url = "/logout"
		state.Action = "Logout"
	}

	templates.ExecuteTemplate(w, "header.tmpl", state)
	templates.ExecuteTemplate(w, "game.tmpl", nil)
	templates.ExecuteTemplate(w, "footer.tmpl", nil)
}

func createSave(username, data string) error {
	var id int
	err := database.QueryRow(`update savedata set data = $1 where username = $2 returning id`, data, username).Scan(&id)
	return err
}

func loadSave(username string) (string, error) {
	var data string
	fmt.Println("loading data for" + username)
	err := database.QueryRow(`SELECT data FROM savedata where username=$1`, username).Scan(&data)
	return data, err
}

func checkLogin(r *http.Request) (string, error) {
	cookie, err := r.Cookie("username")
	if err != nil {
		return "", err
	}

	if cookie.Value == "" {
		return "", errors.New("Invalid username")
	}

	username := cookie.Value

	return username, nil
}

func save(w http.ResponseWriter, r *http.Request) {
	username, err := checkLogin(r)
	if err != nil {
		http.Error(w, "bad!", http.StatusNotAcceptable)
		return
	}

	if r.Method == "POST" {
		r.ParseForm()
		data := r.PostFormValue("data")

		if data == "" {
			http.Error(w, "bad!", http.StatusNotAcceptable)
			return
		}

		err := createSave(username, data)
		if err != nil {
			fmt.Println(err)
			http.Error(w, "bad!", http.StatusNotAcceptable)
			return
		}
	}
}

func load(w http.ResponseWriter, r *http.Request) {
	username, err := checkLogin(r)
	if err != nil {
		http.Error(w, "bad!", http.StatusNotAcceptable)
		fmt.Println(err)
		return
	}

	if r.Method == "POST" {
		data, err := loadSave(username)
		if err != nil {
			fmt.Println(err)
			data = ""
		}
		fmt.Println(data)
		out, err := json.Marshal(data)
		if err != nil {
			fmt.Println(err)
		}
		w.Header().Set("Content-Type", "application/json")
		w.Write(out)
	}
}

func logout(w http.ResponseWriter, r *http.Request) {
	loginCookie := &http.Cookie{Name: "username", Value: "", Expires: time.Now(), Path: "/"}
	http.SetCookie(w, loginCookie)
	http.Redirect(w, r, "/login", http.StatusFound)
}

func index(w http.ResponseWriter, r *http.Request) {
	var state State
	_, err := r.Cookie("username")
	if err != nil {
		state.Url = "/login"
		state.Action = "Login"
	} else {
		state.Url = "/logout"
		state.Action = "Logout"
	}

	templates.ExecuteTemplate(w, "header.tmpl", state)
	templates.ExecuteTemplate(w, "index.tmpl", nil)
	templates.ExecuteTemplate(w, "footer.tmpl", nil)
}

func main() {
	http.HandleFunc("/forum/", forum)
	http.HandleFunc("/newpost/", newpost)
	http.HandleFunc("/post", post)
	http.HandleFunc("/signup", signup)
	http.HandleFunc("/login", login)
	http.HandleFunc("/game", game)
	http.HandleFunc("/save", save)
	http.HandleFunc("/load", load)
	http.HandleFunc("/logout", logout)
	http.HandleFunc("/", index)
	http.Handle("/imgs/", http.StripPrefix("/imgs/", http.FileServer(http.Dir("imgs"))))
	http.Handle("/js/", http.StripPrefix("/js/", http.FileServer(http.Dir("js"))))
	http.ListenAndServe(":8080", nil)
}
<file_sep>/README.md
 <NAME>

<NAME>

<NAME>

<NAME>

# CSE337project
## Requirements
1. Postgresql
2. Go 1.5+

## Setup
1. Update the database.json file to be for your environment
2. run go get to get libpq
3. insert the contents of database.sql
4. go run main.go
<file_sep>/src/js/game.js
var baseHeros = [
    {"name":"hero 1","growth": 0.1, "basedps":1, "dps": 0, "cost": 0.5, "level":0, "img":"imgs/Knight1.png"},
    {"name":"hero 2", "growth": 0.1, "basedps":1, "dps": 0, "cost": 100, "level":0, "img":"imgs/Knight2.png"},
];
var listOfEnemy = [
    {"id":0, "name":"Ghost", "hp": 20, "money": 2, "img":"imgs/one.png"},
    {"id":1, "name":"Poltergeist", "hp": 60, "money": 5, "img":"imgs/two.png"},
    {"id":2, "name":"Phantom", "hp": 100, "money": 8, "img":"imgs/three.png"},
    {"id":3, "name":"<NAME>", "hp": 80, "money": 7, "img":"imgs/Orb.png"},
    {"id":4, "name":"Apeclopse", "hp": 160, "money": 13, "img":"imgs/ManApe.png"},
    {"id":5, "name":"<NAME>", "hp": 40, "money": 4, "img":"imgs/Scorpion.png"},
];

var saveExpire = localStorage.getItem("expire");
var data = null;
var currentEnemy = null;
if(saveExpire == null || saveExpire < (new Date()).getTime()) {
	console.log("Local Save Expired: " + saveExpire);
} else {
	data = JSON.parse(localStorage.getItem("data"));
	currentEnemy = JSON.parse(localStorage.getItem("currentEnemy"));
}
if (data===null) {
    data = {
        "heros" : $.extend(true, {}, baseHeros),
        "money" : 0,
        "clicks" : 0,
        "clickdamage": 1,
        "clickprice" : 1,
	"clicklvl" : 1,
	"clickgrowth" : 0.1,
        "dps": 0
    };
}
if (currentEnemy == null) {
	currentEnemy = $.extend(true, {},listOfEnemy[0]);
}

if (data.clickprice == null) {
	data.clickprice = 15;
}
if (data.clicklvl == null) {
	data.clicklvl = 1;
}
if (data.clickgrowth == null) {
	data.clickgrowth = 0.1;
}


function setnames() {
    $('.heroname').html(function(i, obj) {
        return "<h1><span class=\"label label-default\">" + data.heros[i]["name"] + "</span></h1>";
    });
    $('.upgradename').html(function(i,obj) {
        return "<h1><span class=\"label label-default\">click</span></h1>";
    });
}

function setprice() {
    $('.heroprice').html(function(i, obj) {
        return "<button class=\"btn btn-primary\" onclick=\"buyhero("+i+")\">LVL UP<br />$" + (data.heros[i]["cost"]).toFixed(2) + "</button>";
    });
}

function setclickprice(price) {
   $('.upgradeprice').html(function(i,obj) {
       return "<button class=\"btn btn-primary\" onclick=\"buyClick()\">LVL UP <br />$" + price + "</button>";
   });
}


function buyClick() {
    curdmg = data.clickdamage; 
    price = data.clickprice;
    if(price > data["money"]) {
       console.log("Not enough money");
       return;
    }
    data["money"] -= price;
    data.clickdamage += data.clickgrowth;
    data.clicklvl++;
    data.clickprice = data.clicklvl * data.clicklvl;
    setclickpower();
    setclickprice(data.clickprice.toFixed(2));
    setstats();
}

function setclickpower() {
    $('.upgradelevel').html(function(i,obj) {
       return "<h1><span class=\"label label-default\">Lvl " + data.clicklvl + "</h1>";
    });
}

function setstats() {
    $('.user-stats').html(function(i, obj) {
        return "Money: " + (data["money"]).toFixed(2) + "<br>" +
        "Click power: " + data["clickdamage"].toFixed(2) + "<br>" +
        "Total Hero Power: " + (data["dps"]).toFixed(2) + "<br>";;
    });
}

function setlevel() {
    $('.herolevel').html(function(i, obj) {
        return "<h1><span class=\"label label-default\">Lvl " + data.heros[i]["level"] + "</span></h1>";
    });
}

function buyhero(heroid) {
    hero = data.heros[heroid];
    if(hero["cost"] > data["money"]) {
        console.log("Not enough money");
        return;
    }

    data["money"] -= hero["cost"];
    data.heros[heroid]["level"]++;
    newcost(heroid);
    newdps(heroid);
    setlevel();
    setstats();
}

function newcost(heroid) {
    data.heros[heroid]["cost"] += (data.heros[heroid]["cost"] * 0.1);
    setprice();
}



function newdps(heroid) {
    data.heros[heroid]["dps"] = data.heros[heroid]["basedps"] + data.heros[heroid]["level"]*data.heros[heroid]["growth"];
    dps = 0;
    for (var key in data.heros) {
        dps += data.heros[key]["dps"];
    }

    data["dps"] = dps;
    setstats();
}

function setenemyname() {
    $('.enemyname').each(function(i, obj) {
        obj.textContent = currentEnemy["name"];
    });
}

function setupnewenemy() {
    $('.progress-bar').attr("value", currentEnemy["hp"]);
    if (currentEnemy["id"] != null)
        max = listOfEnemy[currentEnemy["id"]]["hp"];
    else
        max = currentEnemy["hp"];
    $('.progress-bar').attr('max', max);
    setenemyname();
    $('#enemyimg').attr("src", currentEnemy["img"]);
}

function isEnemyDead() {
    if (currentEnemy["hp"] <= 0) {
        data["money"] += currentEnemy["money"];
        i = Math.floor(Math.random() * listOfEnemy.length);
        currentEnemy = $.extend(true, {},listOfEnemy[i]);
        setupnewenemy();
        setstats();
        return true;
    }

    return false;
}

function setEnemyBar() {
    max = $('.progress-bar').attr('max');
    $('.progress-bar').attr("value", currentEnemy["hp"]);
}

function clickdamage() {
    data["clicks"]++;
    causeDamage(data["clickdamage"]);
}

function damagepersecond() {
    causeDamage(data["dps"]);
}

function causeDamage( dmg) {
    currentEnemy["hp"] -= dmg;
    if(isEnemyDead()==true) {
        return;
    }

    setEnemyBar();
}

function savelocalstoarge() {
    localStorage.setItem("data", JSON.stringify(data));
    localStorage.setItem("expire", (new Date()).getTime() + 10000);
    localStorage.setItem("currentEnemy", JSON.stringify(currentEnemy));
}

function saveData() {
    $.ajax({
      type: "POST",
      url: "/save",
      data: "data="+JSON.stringify(data),
      success: function(d, textStatus, jqXHR) { alert ("Game saved!"); },
      error: function(d, textStatus, err) { loginScreen(); }
    });
}

function fixUI() {
	setstats();
	setprice();
	setclickprice(data.clickprice);
	setclickpower();
	setlevel();
	setnames();
}

function loadData() {
    $.ajax({
      type: "POST",
      url: "/load",
      data: "data="+JSON.stringify(data),
      success: function(d,textStatus,jqXHR) { if (d != "") data = JSON.parse(d); fixUI(); alert("Game Loaded"); },
      error: function(d,textStatus,err) { loginScreen(); }
    });
}

function loginScreen() {
    savelocalstoarge();
    window.location.replace("http://localhost:8080/login");
}

setupnewenemy();
fixUI();
dpsInterval = setInterval(damagepersecond, 1000);
saveInterval = setInterval(savelocalstoarge, 1000);
