import 'http://ns.nuke24.net/Schema/comment'
import 'http://ns.nuke24.net/Schema/isNullable'
import 'http://ns.nuke24.net/Schema/defaultValue'
import 'http://ns.nuke24.net/Schema/valueType'
import 'http://ns.nuke24.net/Schema/longName'
import 'http://ns.nuke24.net/Schema/Functions/Unindent'
import 'http://ns.nuke24.net/Schema/Types/Number'
import 'http://ns.nuke24.net/Schema/DataTypeTranslation/sqlType'
import 'http://ns.nuke24.net/Schema/DataTypeTranslation/phpType'
import 'http://ns.nuke24.net/Schema/DataTypeTranslation/jsonType'
import 'http://ns.nuke24.net/Schema/DataTypeTranslation/regex'
import 'http://ns.nuke24.net/Schema/Application/hasADatabaseTable'
import 'http://ns.nuke24.net/Schema/Application/hasARestService'
import 'http://ns.nuke24.net/Schema/Application/membersArePublic'
import 'http://ns.nuke24.net/Schema/RDB/nameInDatabase'
import 'http://ns.nuke24.net/Schema/RDB/Schema'
import 'http://ns.nuke24.net/Schema/RDB/Sequence'
import 'http://ns.nuke24.net/Schema/RDB/initialValue'
import 'http://ns.nuke24.net/Schema/RDB/defaultValueSequence'
import 'http://ns.nuke24.net/Schema/RDB/isAutoIncremented'
import 'http://ns.nuke24.net/Schema/RDB/isInNamespace'
import 'http://ns.nuke24.net/Schema/RDB/isSelfKeyed'
import 'http://schema.org/True' as 'true'
import 'http://schema.org/False' as 'false'
import 'http://www.w3.org/2000/01/rdf-schema#isSubclassOf' as 'extends'

class 'integer' :
        SQL type @ "INT" :
        PHP type @ "int" : JSON type @ "number"
class 'unsigned integer' : extends(integer) :
	SQL type @ "INT UNSIGNED" : regex @ "\\d+"
class 'boolean' :
        SQL type @ "BOOLEAN" :
        PHP type @ "bool" : JSON type @ "boolean"
class 'string' :
        SQL type @ "VARCHAR(126)" :
        PHP type @ "string" : JSON type @ "string"
class 'normal ID' : extends(unsigned integer)
class 'entity ID' : extends(unsigned integer) : PHP type @ "string" : SQL type @ "BIGINT"
class 'code' : extends(string) : SQL type @ "CHAR(4)" : regex @ "[A-Za-z0-9 _-]{1,4}"
class 'text' : extends(string) : SQL type @ "TEXT"
class 'HTML fragment' : extends(text) : comment @ "a block of HTML"
class 'hash' : extends(string) : regex @ "[A-Fa-f0-9]{40}" : comment @ "Hex-encoded SHA-1 of something (40 bytes)"
class 'e-mail address' : extends(string) : regex @ "[^\\s@]+@[^\\s@]+"
class 'URI' : extends(string)
class 'time' : extends(string) : SQL type @ "TIMESTAMP"
class 'date' : extends(string) : SQL type @ "DATE"
# Address components
class 'postal code' : extends(string)
class 'region code' : extends(string) :
	SQL type @ "CHAR(2)" :
	regex @ "[A-Z]{2}" :
	comment @ "2-letter state code."
class 'country code' : extends(string) :
	SQL type @ "CHAR(3)" :
	regex @ "[A-Z]{3}" :
	comment @ "ISO 3166-1 alpha-3 3-letter country code."
# Large ID types
class 'blob ID' : extends(string) :
        SQL type @ "CHAR(32)" :
        regex @ "[A-Z2-7]{32}" :
        comment @ "Base32-encoded SHA-1 of the blob"
class 'picture ID' : extends(blob ID)

# TODO: Make these more officially part of the Schema/Application schema
class property 'records are immutable' : value type @ boolean :
	long name @ "http://ns.nuke24.net/Schema/Application/recordsAreImmutable" :
	comment @ "Indicates that records of this class are immutable, once written."
field property 'value is calculated' : value type @ boolean :
	long name @ "http://ns.nuke24.net/Schema/Application/valueIsCalculated" :
	comment @ "Indicates that the field's value is a function of other data, so cannot be edited directly."
field property 'uses user text canonicalization rules' :
	long name @ "http://ns.nuke24.net/Schema/Application/usesUserTextCanonicalizationRules" :
	comment @ "Means text is stored trimmed, and null when empty for nullable fields"
field property 'is content hash-based ID' :
	long name @ "http://ns.nuke24.net/Schema/RDB/isContentHashBasedId" :
	comment @ unindent @ «
		Applies to primary key fields whose value is calculated based on the primary content of the record,
		making the record by definition immutable.
		Mutable metadata (last access time, etc) may be stored
		with the record as long as not included with the hash.
	»

schema 'phptemplateprojectdatabasenamespace'

sequence 'new entity ID' : initial value @ 1001 : is in namespace @ 'phptemplateprojectdatabasenamespace' :
	long name @ "urn:x-the-new-entity-id-sequence" # Meaningless long name for schema.php

field modifier 'AIPK' = normal ID : is auto-incremented : key(primary)
field modifier 'EIPK' = entity ID : default value sequence @ new entity ID : key(primary)
field modifier 'HJPK' = blob ID : is content hash-based ID : key(primary) : comment @ "ID = base32encode(sha1(json(canonocalize(record))))"
# SRC = 'standard resource class'
field modifier 'SRC' = has a database table : has a REST service : is in namespace @ 'phptemplateprojectdatabasenamespace'

field modifier UTCR = uses user text canonicalization rules

class 'user' : SRC {
	ID : EIPK
	username : string
	passhash : hash : nullable
	e-mail address : e-mail address : nullable
}

class 'token action' : has a database table : is in namespace @ 'phptemplateprojectdatabasenamespace' {
	token hash : hash : key(primary)
	half user : reference(user) : comment @ unindent @ «
		User on whose beHALF the action will be done.
	» = ID = half user ID
	action script : text
	re-useable : boolean
	expiration time : time : nullable
	usage time : time : nullable
}

class 'postal address' : records are immutable : SRC {
	ID : HJPK
	street address : string : nullable : UTCR
	unit address : string : nullable : UTCR
	city name : string : nullable : UTCR
	region code : region code : nullable : UTCR
	postal code : postal code : nullable : UTCR
	country code : country code : nullable : UTCR
}

class 'organization' : SRC : members are public {
	ID : EIPK
	name : string
}

class 'entity postal address' : SRC : self-keyed {
	entity ID : entity ID
	address : reference(postal address) = ID = postal address ID
}

class 'user organization attachment' : SRC : self-keyed {
	# first 'user' is the name of the property of a 'user organization attachment'
	# reference(user) means that this property is a reference to a 'user' record
	# { ID = user ID } means the 'ID' of the 'user' record matches the 'user ID' field of this record
	user : reference(user) {
		ID = user ID
	}
	organization : reference(organization) {
		ID = organization ID
	}
}

# Actual enums are hard.
# Best practice according to Dan is avoid defining enum types and just always use a table.
class 'computation status' : SRC : members are public {
	status code : string : key(primary)
}

class 'computation' : SRC : members are public {
	expression : string : key(primary)
	status : reference(computation status) = status code
	result : string : nullable
}

# Demonstrate an order system or something

class 'product' : SRC : members are public {
	ID : EIPK
	UPC : string
	title : string
	description HTML : HTML fragment
}

class 'order' : SRC {
	ID : EIPK
	user : reference(user) = ID = user ID
	shipping address : reference(postal address) = ID = shipping address ID
	billing address : reference(postal address) = ID = billing address ID
}

class 'order item' : SRC {
	order : reference(order) = ID = order ID
	product : reference(product) = ID = product ID
	quantity : integer : default value @ 1
}
