package goPress

//
// Package:          goPress
//
// Description:      This package is for the goPress CMS written in the go programming
//                           language made by Google. This package defines everything for a full
//                           CMS that takes new submissions from a DropBox account and applies it
//                           to the web site.
//

//
// Import the libraries we use for this program.
//
import (
	"encoding/json"
	"github.com/hoisie/web"
	"io/ioutil"
	"log"
	"os"
	"strings"
)

//
// Define a structure to contain all the information important to the CMS. Capticalized
// variables within the structure is imported and exported.
//
type goPressData struct {
	CurrentLayout  string
	CurrentStyling string
	ServerAddress  string
	SiteTitle      string
	Sitebase       string
	TemplatBase    string
	CapatchaWidth  int
	CapatchaHeight int
	Cache          bool
	MainBase       string
	content        map[string]string
	layoutBase     string
	mainpg         string
	postbase       string
	scripts        string
	stylesheet     string
	stylingBase    string
	template       string
}

var SiteData = new(goPressData)
var ServerParamFile string = "server.json"

//
// Function:          GetGlobals
//
// Description:       This function is used to create the global variables initialize the
//                            global variables.
//
// Inputs:
//
func GetGlobals() {
	log.Println("Loading the defaults...")

	//
	// Load the Server Parameters from a file.
	//
	LoadServerParameters()

	//
	// Setup the basic paths to everything.
	//
	SiteData.layoutBase = SiteData.TemplatBase + "layouts/"
	SiteData.stylingBase = SiteData.TemplatBase + "styling/"
	SiteData.postbase = SiteData.Sitebase + "posts/"

	//
	// Create the content array that will hold the site fragments. Set the title
	// now.
	//
	SiteData.content = make(map[string]string)
	SiteData.content["title"] = SiteData.SiteTitle
	log.Println("Loading data for site: " + SiteData.SiteTitle)

	//
	// Get all the basic information that is generic and in the styles and layout directories.
	// These will then be over written if a new default in the site area is found. This gives
	// the flexibility to load defaults from a directory without having to make sure that all
	// the necessary ones are loaded.
	//

	//
	// Get the 404 page contents
	//
	SiteData.content["404"] = GetPageContents(SiteData.stylingBase + SiteData.CurrentStyling + "/404")

	//
	// Get the sidebar contents
	//
	SiteData.content["sidebar"] = GetPageContents(SiteData.stylingBase + SiteData.CurrentStyling + "/sidebar")

	//
	// Get the footer contents
	//
	SiteData.content["footer"] = GetPageContents(SiteData.stylingBase + SiteData.CurrentStyling + "/footer")

	//
	// Get the template contents
	//
	SiteData.template = GetPageContents(SiteData.layoutBase + SiteData.CurrentLayout + "/template")

	//
	// Get the header contents
	//
	SiteData.content["header"] = GetPageContents(SiteData.stylingBase + SiteData.CurrentStyling + "/header")

	//
	// Get the main page contents
	//
	SiteData.mainpg = GetPageContents(SiteData.Sitebase + "pages/" + "main")

	//
	// The following will load page parts from the "parts" directory for the site. These might
	// overload those already defined or add new stuff that the users site templates
	// will need.
	//
	partsdir := SiteData.Sitebase + "parts/"

	//
	// Read the directory.
	//
	fileList, err := ioutil.ReadDir(partsdir)
	if err != nil {
		//
		// Error reading the directory.
		//
		log.Printf("Error reading directory: %s\n", partsdir)
	} else {
		//
		// Get the number of items in the directory list.
		//
		count := len(fileList)

		//
		// Loop through each directory element.
		//
		for i := 0; i < count; i++ {
			if !fileList[i].IsDir() {
				//
				// It is a file. Read it and add to the scripts variable.
				//
				filename := fileList[i].Name()
				parts := strings.Split(filename, ".")
				if filename != ".DS_Store" {
					SiteData.content[parts[0]] = LoadFile(partsdir + filename)
				}
			}
		}
	}

	//
	// Clear out the global variables not set.
	//
	SiteData.scripts = ""
	SiteData.stylesheet = ""
}

//
// Function:            SaveServerParameters
//
// Description:       This function is for saving the authorization secret for DropBox.
//
// Inputs:
//
func SaveServerParameters() {
	if wfile, err := os.Create(ServerParamFile); err == nil {
		enc := json.NewEncoder(wfile)
		enc.Encode(&SiteData)
		wfile.Close()
	} else {
		log.Println("Writing Server file denied.")
	}
}

//
// Function:          LoadServerParameters
//
// Description:      This function is used to load the parameters for this server.
//
// Inputs:
//
func LoadServerParameters() {
	if wfile, err := os.Open(ServerParamFile); err == nil {
		enc := json.NewDecoder(wfile)
		enc.Decode(&SiteData)
		wfile.Close()
		log.Println("Read the " + ServerParamFile + " server parameter file. Site Title is: " + SiteData.SiteTitle)
	} else {
		log.Println("No Server File found.")
	}
}

//
// Function:          SiteReset
//
// Description:       This function creates a site wide reset to base state. This
//                            is done by doing a sync and reloading all global variables
//                            from the files.
//
// Inputs:
//
func SiteReset(ctx *web.Context) string {
	GetGlobals()         // Reload all global variables
	return Mainpage(ctx) // Return the main page for the site
}

//
// Function:          DefaultRoutes
//
// Description:       This function sets the default routes for a CMS.
//
// Inputs:
//
func DefaultRoutes() {
	SetGetRoute("/", Mainpage)
	SetGetRoute("/sitemap.xml", SiteMap)
	SetGetRoute("/stylesheets.css", GetStylesheets)
	SetGetRoute("/scripts.js", GetScripts)
	SetGetRoute("/theme/images/(.*)", LoadThemeImage)
	SetGetRoute("/(favicon.ico)", ImagesLoad)
	SetGetRoute("/images/(.*)", ImagesLoad)
	SetGetRoute("/posts/([a-zA-Z0-9]*)/([a-zA-Z0-9]*)", PostIndex)
	SetGetRoute("/posts/([a-zA-Z0-9]*)/([a-zA-Z0-9]*)/(.*)", PostPages)
	SetGetRoute("/(.*)", TopPages)
}

//
// Function:           SetGetRoute
//
// Description:       This function gives an easy access to the web variable setup in this library.
//
// Inputs:
// 		route 	Route to setup
//		handler      Function to run that route.
//
func SetGetRoute(route string, handler interface{}) {
	web.Get(route, handler)
}

//
// Function:           SetPostRoute
//
// Description:       This function gives an easy access to the web variable setup in this library.
//
// Inputs:
// 		route 	Route to setup
//		handler      Function to run that route.
//
func SetPostRoute(route string, handler interface{}) {
	web.Post(route, handler)
}

//
// Function:          StartServer
//
// Description:       This function is for starting the web server using
//                            the SiteData configuration.
//
// Inputs:
//
func StartServer(serverAddress string) {
	web.Run(serverAddress)
}
