//Show builds an html page that display a receipt
//this is a very boring, plain test, monospaced font page designed for easy printing and reading
//the receipt is generated from the charge id
//the data for the charge may be in memcache or will have to be retrieved from stripe
func Show(w http.ResponseWriter, r *http.Request) {
	//get charge id from form value
	chargeId := r.FormValue("chg_id")

	//try looking up charge data in memcache
	var chg *stripe.Charge
	c := appengine.NewContext(r)
	_, err := memcache.Gob.Get(c, chargeId, &chg)

	//charge not found in memcache
	//look up charge data from stripe
	if err == memcache.ErrCacheMiss {
		//init stripe
		c := appengine.NewContext(r)
		stripe.SetBackend(stripe.APIBackend, nil)
		stripe.SetHTTPClient(urlfetch.Client(c))

		//get charge data
		chg, err = charge.Get(chargeId, nil)
		if err != nil {
			fmt.Fprint(w, "An error occured and the receipt cannot be displayed.\n")
			fmt.Fprint(w, err)
			return
		}

		//save to memcache
		//just in case we want to view the receipt again
		memcacheutils.Save(c, chg.ID, chg)
	}

	//extract charge data
	d := chargeutils.ExtractData(chg)

	//get company info
	info, err := getCompanyInfo(r)
	name, street, suite, city, state, postal, country, phone := "", "", "", "", "", "", "", ""
	if err == ErrCompanyDataDoesNotExist {
		name = "**Company info has not been set yet.**"
		street = "**Please contact an administrator to fix this.**"
	} else {
		name = info.CompanyName
		street = info.Street
		suite = info.Suite
		city = info.City
		state = info.State
		postal = info.PostalCode
		country = info.Country
		phone = info.PhoneNum
	}

	//display receipt
	output := templateData{
		CompanyName: name,
		Street:      street,
		Suite:       suite,
		City:        city,
		State:       state,
		Postal:      postal,
		Country:     country,
		PhoneNum:    phone,
		Customer:    d.Customer,
		Cardholder:  d.Cardholder,
		CardBrand:   d.CardBrand,
		LastFour:    d.LastFour,
		Expiration:  d.Expiration,
		Captured:    d.CapturedStr,
		Timestamp:   d.Timestamp,
		Amount:      d.AmountDollars,
		Invoice:     d.Invoice,
		Po:          d.Po,
	}
	templates.Load(w, "receipt", output)
	return
}
//Report gets the data for charges and refunds by the defined filters (date range and customer) and builds the reports page
//the reports show up in a different page so they are easily printable and more easily inspected
//date range is inclusive of start and end days
func Report(w http.ResponseWriter, r *http.Request) {
	//get form valuess
	datastoreId := r.FormValue("customer-id")
	startString := r.FormValue("start-date")
	endString := r.FormValue("end-date")
	hoursToUTC := r.FormValue("timezone")

	//get report data form stripe
	//make sure inputs are given
	if len(startString) == 0 {
		output.Error(ErrMissingInput, "You must supply a 'start-date'.", w, r)
		return
	}
	if len(endString) == 0 {
		output.Error(ErrMissingInput, "You must supply a 'end-date'.", w, r)
		return
	}
	if len(hoursToUTC) == 0 {
		output.Error(ErrMissingInput, "You must supply a 'timezone'.", w, r)
		return
	}

	//get timezone offset
	//adjust for the local timezone the user is in so that the date range is correct
	//hoursToUTC is a number generated by JS (-4 for EST)
	tzOffset := calcTzOffset(hoursToUTC)

	//get datetimes from provided strings
	startDt, err := time.Parse("2006-01-02 -0700", startString+" "+tzOffset)
	if err != nil {
		output.Error(err, "Could not convert start date to a time.Time datetime.", w, r)
		return
	}
	endDt, err := time.Parse("2006-01-02 -0700", endString+" "+tzOffset)
	if err != nil {
		output.Error(err, "Could not convert end date to a time.Time datetime.", w, r)
		return
	}

	//get end of day datetime
	//need to get 23:59:59 so we include the whole day
	endDt = endDt.Add((24*60-1)*time.Minute + (59 * time.Second))

	//get unix timestamps
	//stripe only accepts timestamps for filtering charges
	startUnix := startDt.Unix()
	endUnix := endDt.Unix()

	//init stripe
	c := appengine.NewContext(r)
	sc := createAppengineStripeClient(c)

	//retrieve data from stripe
	//date is a range inclusive of the days the user chose
	//limit of 100 is the max per stripe
	params := &stripe.ChargeListParams{}
	params.Filters.AddFilter("created", "gte", strconv.FormatInt(startUnix, 10))
	params.Filters.AddFilter("created", "lte", strconv.FormatInt(endUnix, 10))
	params.Filters.AddFilter("limit", "", "100")

	//check if we need to filter by a specific customer
	//look up stripe customer id by the datastore id
	if len(datastoreId) != 0 {
		datastoreIdInt, _ := strconv.ParseInt(datastoreId, 10, 64)
		custData, err := findByDatastoreId(c, datastoreIdInt)
		if err != nil {
			output.Error(err, "An error occured and this report could not be generated.", w, r)
			return
		}

		params.Filters.AddFilter("customer", "", custData.StripeCustomerToken)
	}

	//get results
	//loop through each charge and extract charge data
	//add up total amount of all charges
	charges := sc.Charges.List(params)
	data := make([]chargeutils.Data, 0, 10)
	var amountTotal uint64 = 0
	var numCharges uint16 = 0
	for charges.Next() {
		//get each charges data
		chg := charges.Charge()
		d := chargeutils.ExtractData(chg)

		//make sure this charge was captured
		//do not count charges that failed
		if d.Captured == false {
			continue
		}

		data = append(data, d)

		//increment totals
		amountTotal += d.AmountCents
		numCharges++
	}

	//convert total amount to dollars
	amountTotalDollars := strconv.FormatFloat((float64(amountTotal) / 100), 'f', 2, 64)

	//retrieve refunds
	eventParams := &stripe.EventListParams{}
	eventParams.Filters.AddFilter("created", "gte", strconv.FormatInt(startUnix, 10))
	eventParams.Filters.AddFilter("created", "lte", strconv.FormatInt(endUnix, 10))
	eventParams.Filters.AddFilter("limit", "", "100")
	eventParams.Filters.AddFilter("type", "", "charge.refunded")

	events := sc.Events.List(eventParams)
	refunds := chargeutils.ExtractRefunds(events)

	//get logged in user's data
	//for determining if receipt/refund buttons need to be hidden or shown based on user's access rights
	session := sessionutils.Get(r)
	userId := session.Values["user_id"].(int64)
	userdata, _ := users.Find(c, userId)

	//store data for building template
	result := reportData{
		UserData:    userdata,
		StartDate:   startDt,
		EndDate:     endDt,
		Charges:     data,
		Refunds:     refunds,
		TotalAmount: amountTotalDollars,
		NumCharges:  numCharges,
	}

	//build template to display report
	//separate page in gui
	templates.Load(w, "report", result)
	return
}