// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"strings"

	"github.com/googleapis/gnostic/printer"
)

// ProtoOption represents an option to be added to generated .proto files.
type ProtoOption struct {
	Name    string
	Value   string
	Comment string
}

func (domain *Domain) generateProto(packageName string, license string, options []ProtoOption, imports []string) string {
	code := &printer.Code{}
	code.Print(license)
	code.Print("// THIS FILE IS AUTOMATICALLY GENERATED.")
	code.Print()

	code.Print("syntax = \"proto3\";")
	code.Print()
	code.Print("package " + packageName + ";")
	for _, importString := range imports {
		code.Print()
		code.Print("import \"" + importString + "\";")
	}
	code.Print()

	// generate option declarations
	for _, option := range options {
		commentLines := strings.Split(option.Comment, "\n")
		for _, commentLine := range commentLines {
			code.Print(commentLine)
		}
		line := "option " + option.Name + " = "
		if option.Value == "true" || option.Value == "false" {
			line += option.Value
		} else {
			line += "\"" + option.Value + "\""
		}
		line += ";\n"
		code.Print(line)
	}

	// generate message definitions
	typeNames := domain.sortedTypeNames()
	for _, typeName := range typeNames {
		typeModel := domain.TypeModels[typeName]
		if typeModel.Description != "" {
			code.Print("// %s", typeModel.Description)
		}
		code.Print("message %s {", typeName)
		code.Indent()
		if typeModel.OneOfWrapper {
			code.Print("oneof oneof {")
			code.Indent()
		}
		var fieldNumber = 0
		for _, propertyModel := range typeModel.Properties {
			if propertyModel.Description != "" {
				code.Print("// %s", propertyModel.Description)
			}
			propertyName := propertyModel.Name
			fieldNumber++
			propertyType := propertyModel.Type
			if propertyType == "int" {
				propertyType = "int64"
			}
			if propertyType == "float" {
				propertyType = "double"
			}

			// TODO may be remove this.
			if propertyType == "blob" {
				propertyType = "string"
			}

			var displayName = propertyName
			if displayName == "$ref" {
				displayName = "_ref"
			}
			if displayName == "$schema" {
				displayName = "_schema"
			}
			displayName = camelCaseToSnakeCase(displayName)

			var line = fmt.Sprintf("%s %s = %d;", propertyType, displayName, fieldNumber)
			if propertyModel.Repeated {
				line = "repeated " + line
			}
			code.Print(line)
		}
		if typeModel.OneOfWrapper {
			code.Outdent()
			code.Print("}")
		}
		code.Outdent()
		code.Print("}")
		code.Print()
	}
	return code.String()
}
