local c = game.Workspace.Terrain
local SetCell = c.SetCell
local SetWaterCell = c.SetWaterCell
local GetCell = c.GetCell
local GetWaterCell = c.GetWaterCell
local WorldToCellPreferSolid = c.WorldToCellPreferSolid
local CellCenterToWorld = c.CellCenterToWorld

-----------------
--DEFAULT VALUES-
-----------------
loaded = false
-- Hold river path points.
x1 = 200
y1 = 200
x2 = 300
y2 = 300
h = Workspace.Terrain.Size.y

-- Used for testing.  Points can be clicked instead of dragged.
allowClicking = false

-- True if the plugin is on, false if not.
on = false

-- Max height that terrain can be removed from above water.
maxTunnelHeight = 30

-- Maximum thickness the user is allowed to make the river.  This should always be a odd number.
maxRiverThickness = 30

-- Maximum width the user is allowed to make the river.  This should always be a odd number.
maxRiverWidth = 20

-- ID number that represents water material.  Use this instead of a magic number.
waterMaterialID = 17

-- How tall the waypoints should be when drawn.
waypointDrawSizeY = 20

-- Maximum number of steps that can be appled to a river section.
maxRiverSteps = 8

-- Represent the different ways a river draws.
-- Use to correctly join continuous river sections.
decreaseX = 'DecreaseX'
increaseX = 'IncreaseX'
decreaseZ = 'DecreaseZ'
increaseZ = 'IncreaseZ'
noChange = 'None'

waterForceNone = 'None'
waterForceSmall = 'Small'
waterForceMedium = 'Medium'
waterForceLarge = 'Large'
waterForceMax = 'Max'

-- Used by water to set the strength.  Stored in the order to use.
FORCE_NAMES = {
	waterForceNone,
	waterForceSmall,
	waterForceMedium,
	waterForceLarge,
	waterForceMax,
}

-- Used by water to set the flow direction.
DIRECTION_NAMES = {
	"Negative X",
	"Positive X",
	"Negative Y",
	"Positive Y",
	"Negative Z",
	"Positive Z",
}

-- Flow directions.
flowPositiveX = "Positive X"
flowNegativeX = "Negative X"
flowPositiveY = "Positive Y"
flowNegativeY = "Negative Y"
flowPositiveZ = "Positive Z"
flowNegativeZ = "Negative Z"
flowStartToEnd = 'Start to End'
flowEndToStart = 'End to Start'	-- This wouldn't work unless I changed path drawing from always doing x first to do y first in this case.  Don't use now, maybe in the future...

heightStatic = 'Static'				-- Water stays at the height of the start of the river.
heightFitTerrain = 'FitTerrain'		-- Water height tries to contour to fit the terrain.

-- When river points gets used to draw the river.
drawImmediate = 'Immediate'		-- Draw the river points as soon as they are clicked.
drawWait = 'Wait'				-- Wait to use the river points until told to draw with them.

-- What can be turned to water.
changeTerrain = 'Terrain'			-- Only terrain can be turned to water.
changeAir = 'Air'					-- Only air can be turned to water.
changeTerrainAir = 'TerrainAir'	-- Terrain and air can be turned to water.

-- Represent the different ways that a river can be filled in.  
fillModeUp = 'Up'				-- The start point is considered the base of the river.
fillModeDown = 'Down'			-- The start point is considered the top of the river.
fillModeCentered = 'Centered'	-- The start point is considered the center of the river.

currentWaypointColor = BrickColor.new("Lime green")
mouseHighlightColor = BrickColor.new("Really red")
waypointColor = BrickColor.new("Toothpaste")


-- Find the water force ID number that goes with the given force name.
-- Return:
-- Value is nil if a match wasn't found.
function ForceToID(name)
	for i = 1, #FORCE_NAMES, 1 do
		if FORCE_NAMES[i] == name then
			return i-1
		end
	end

	return nil
end

-- Find the water direction ID number that goes with the given flow direction name.
-- Return:
-- Value is nil if a match wasn't found.
function WaterDirectionToID(name)
	for i = 1, #DIRECTION_NAMES, 1 do
		if DIRECTION_NAMES[i] == name then
			return i-1
		end
	end

	return nil
end

-- Converts a number ID to a water string ID.
-- numID - Number ID that represents a direction.
-- Return: 
-- String that represents the direction or nil if a match isn't found.
function WaterDirectionIDToName(numID)
	return DIRECTION_NAMES[numID+1]
end

-- Determine which direction water should flow in the x direction.
-- x1 - Start X		-- Initial water position
-- x2 - End X		-- Ending water position.
function DetermineDirX(x1, x2)
	if x1 < x2 then
		return WaterDirectionToID(flowPositiveX)
	elseif x1 > x2 then
		return WaterDirectionToID(flowNegativeX)
	else
		return nil
	end
end

-- Determine which direction water should flow in the z direction.
-- z1 - Start Z		-- Initial water position
-- z2 - End Z		-- Ending water position.
function DetermineDirZ(z1, z2)
	if z1 < z2 then
		return WaterDirectionToID(flowPositiveZ)
	elseif z1 > z2 then
		return WaterDirectionToID(flowNegativeZ)
	else
		return nil
	end
end

-- Reverses the given flow direction.
-- direction - String ID that represents the flow.
-- Return:
-- Value is the reversed direction, if possible. If not, the original is returned.
function ReverseFlow(direction)
	if flowPositiveX == direction then
		return flowNegativeX
	elseif flowNegativeX == direction then
		return flowPositiveX
	elseif flowPositiveY == direction then
		return flowNegativeY
	elseif flowNegativeY == direction then
		return flowPositiveY
	elseif flowPositiveZ == direction then
		return flowNegativeZ
	elseif flowNegativeZ == direction then
		return flowPositiveZ		
	else
		return direction
	end
end

-- Used to create a highlighter.
-- A highlighter is a open, rectuangular area displayed in 3D.
Highlighter = {}
Highlighter.__index = Highlighter

-- Store values needed for canceling river creation
cancelValues = {timeToYield = 0,	  -- What time the next yield should happen at when drawing the river.  Use tick() to get system time.
				timeToWait = .05,	  -- How long to wait, in seconds, between yields.
				cancelAction = false, -- Used to cancle currently occuring actions.  If set to true then river making will be stopped.
				progressBar = nil, 	  -- Will store the progress bar when needed.
				setAmountFunc = nil,  -- Stores a function tied to the progres bar that sets the progress bar precentage done.
				bindForCancel = nil}  -- Stores a function bind that will be set with the function to run when the cancel button is pressed in the progress bar.

-- Used when drawing a river.  Decides if enough time has passed to yield.
function HandleDrawYield()
	if tick() >= cancelValues.timeToYield then
		coroutine.yield()
		cancelValues.timeToYield = tick() + cancelValues.timeToWait
	end
end				
				
-- Create a highlighter object.
--
-- position - Where the highlighter should be displayed.  If nil, the highlighter will not be shown immediatly.
-- ID       - Number ID to use for the box. 
function Highlighter.Create(position, 
							ID)
	-- This will be the created highlighter.
	local highlighter = {}
	
	-- Create the part that the highlighter will be attached to.
	highlighter.selectionPart = Instance.new("Part")
	highlighter.selectionPart.Name = "SelectionPart"
	highlighter.selectionPart.Archivable = false
	highlighter.selectionPart.Transparency = 1
	highlighter.selectionPart.Anchored = true
	highlighter.selectionPart.Locked = true
	highlighter.selectionPart.CanCollide = false
	highlighter.selectionPart.FormFactor = Enum.FormFactor.Custom

	highlighter.selectionBox = Instance.new("SelectionBox")
	highlighter.selectionBox.Name = 'box' .. ID
	
	highlighter.selectionBox.Archivable = false
	highlighter.selectionBox.Color = BrickColor.new("Toothpaste")
	highlighter.selectionBox.Adornee = highlighter.selectionPart
	setmetatable(highlighter, Highlighter)
	
	--print('created ' .. highlighter.selectionBox.Name)
	
	return highlighter
end	

-- Hide the highlighter.
function Highlighter:DisablePreview()
	self.selectionBox.Parent = nil
end

-- Show the highlighter.
function Highlighter:EnablePreview()
	self.selectionBox.Parent = game:GetService("CoreGui") -- This will make it not show up in workspace.
end	
	
-- Update where the highlighter is displayed.
-- cellPos - Where to display the highlighter, in cells.
function Highlighter:UpdatePosition(cellPos)
	--print('Start update')
	if not cellPos then 
		self:DisablePreview()
		return 
	end

	local regionToSelect = nil

	local lowVec = CellCenterToWorld(c, cellPos.x , cellPos.y - 1, cellPos.z)
	local highVec = CellCenterToWorld(c, cellPos.x, cellPos.y + waypointDrawSizeY, cellPos.z)
	regionToSelect = Region3.new(lowVec, highVec)

	self.selectionPart.Size = regionToSelect.Size - Vector3.new(-4,4,-4)
	self.selectionPart.CFrame = regionToSelect.CFrame
	
	self:EnablePreview()
end	

-- Used to create a highlighter that follows the mouse.
-- It is a class mouse highlighter.  To use, call MouseHighlighter.Create(mouse) where mouse is the mouse to track.
MouseHighlighter = {}
MouseHighlighter.__index = MouseHighlighter

-- Create a mouse movement highlighter.
-- plugin - Plugin to get the mouse from.
function MouseHighlighter.Create(mouseUse)
	local highlighter = {}
	
	local mouse = mouseUse
	highlighter.OnClicked = nil
	highlighter.mouseDown = false
	
	-- Store the last point used to draw.
	highlighter.lastUsedPoint = nil
	
	-- Current mouse y.
	local mouseY = 0	
	
	-- Will hold a part the highlighter will be attached to.  This will be moved where the mouse is.
	highlighter.selectionPart = nil

	-- Hook the mouse up to check for movement.
	mouse.Move:connect(function() MouseMoved() end)	
	
	mouse.Button1Down:connect(function() highlighter.mouseDown = true end)
	mouse.Button1Up:connect(function() highlighter.mouseDown = false
									  if not allowClicking then
										  EndCurrentRiver()
									  end
                            end)
	
	-- Create the part that the highlighter will be attached to.
	highlighter.selectionPart = Instance.new("Part")
	highlighter.selectionPart.Name = "SelectionPart"
	highlighter.selectionPart.Archivable = false
	highlighter.selectionPart.Transparency = 1
	highlighter.selectionPart.Anchored = true
	highlighter.selectionPart.Locked = true
	highlighter.selectionPart.CanCollide = false
	highlighter.selectionPart.FormFactor = Enum.FormFactor.Custom

	highlighter.selectionBox = Instance.new("SelectionBox")
	highlighter.selectionBox.Archivable = false
	highlighter.selectionBox.Color = mouseHighlightColor
	highlighter.selectionBox.Adornee = highlighter.selectionPart
	mouse.TargetFilter = highlighter.selectionPart	
	setmetatable(highlighter, MouseHighlighter)

	-- Function to call when the mouse has moved.  Updates where to display the highlighter.
	function MouseMoved()
		if on then
			UpdatePosition(mouse.Hit)
		end
	end

	-- Update where the highlighter is displayed.
	-- position - Where to display the highlighter, in world space.
	function UpdatePosition(position)
		if not position then 
			return 
		end
		
		if not mouse.Target then 
			highlighter.selectionBox.Parent = nil
			return 
		end
		
		highlighter.selectionBox.Parent = game:GetService("CoreGui")
		
		local vectorPos = Vector3.new(position.x,position.y,position.z)
		local cellPos = WorldToCellPreferSolid(c, vectorPos)

		local regionToSelect = nil

		local lowVec = CellCenterToWorld(c, cellPos.x , cellPos.y - 1, cellPos.z)
		local highVec = CellCenterToWorld(c, cellPos.x, cellPos.y + waypointDrawSizeY, cellPos.z)
		regionToSelect = Region3.new(lowVec, highVec)

		highlighter.selectionPart.Size = regionToSelect.Size - Vector3.new(-4*currentRiverOptions.riverWidth, 4, -4*currentRiverOptions.riverWidth)
		highlighter.selectionPart.CFrame = regionToSelect.CFrame
		
		if nil ~= highlighter.OnClicked and highlighter.mouseDown then
			-- Determine if enough movement has occured.
			-- Must be the min distance in at least one direction.
			if nil == highlighter.lastUsedPoint then 
				highlighter.lastUsedPoint = WorldToCellPreferSolid(c, Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.Hit.z))
			else
				local minDist = 4
				cellPos = WorldToCellPreferSolid(c, Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.Hit.z))
				
				holdChange = cellPos - highlighter.lastUsedPoint
				if (math.abs(holdChange.x) >= minDist) or (math.abs(holdChange.y) >= minDist) then
				--if (math.abs(holdChange.x) >= minDist) or (math.abs(holdChange.y) >= minDist) or (math.abs(holdChange.z) >= minDist) then  -- This will run worse.  It will get stuck on small waterfalls.
			
					-- Require clicking on solid ground.
					if 0 == GetCell(c, cellPos.X, cellPos.Y, cellPos.Z).Value then
						return
					else
						highlighter.lastUsedPoint = cellPos
						highlighter.OnClicked(mouse)
					end
				end
			end
		end		
	end	
	
	return highlighter
end

-- Hide the highlighter.
function MouseHighlighter:DisablePreview()
	self.selectionBox.Parent = nil
end

-- Show the highlighter.
function MouseHighlighter:EnablePreview()
	self.selectionBox.Parent = game:GetService("CoreGui") -- This will make it not show up in workspace.
end	
	
-- Create a waypoint. Contains everything needed to know how to draw a section of river.
Waypoint = {}
Waypoint.__index = Waypoint

-- This will create with default options.
function Waypoint.Create()
   local options = {} 
   setmetatable(options, Waypoint)
   
	-- Whether the river should fill what is above it.
	-- If true, the rivers uses a set thickness and material, and sets all terrain above it to the specified thickness to be the material.
	-- This can be used to dig a tunnel by setting the material to be air.   
   options.fillAboveRiver = true
   
	-- How strong water flow is.
	options.riverForce = ForceToID(waterForceMedium)

	-- Which direction water will flow.  By default it is where the user says.
	options.riverDirection = flowStartToEnd

	-- Which direction waterfall water should go.
	options.waterfallDirection = flowStartToEnd

	-- What material to use to fill in above the river.  This should be air.
	options.tunnelMaterial = 0

	-- How much terrain above the water should be removed when enabled.
	options.tunnelHeight = 31

	-- Weither the next click will be the start of a river or the end/continuation of a river.  Value is true for the start of a river and false for the end.
	options.startRiver = true

	-- How thick the river is.  This should always be a odd number (1, 3, 5, etc).
	options.riverThickness = 7

	-- How wide the river is.  This should always be a odd number(1, 3, 5, etc).
	options.riverWidth = 7

	-- How many steps each river segement is broken up into.
	options.riverSteps = 5

	-- How the river will be filled in.  From top to bottom, bottom to top, etc.
	options.fillRiverMode = fillModeDown

	-- Current mode stating what can be turned into water when creating a river.
	-- Default is everything.
	options.changeToWaterMode = changeTerrainAir

	-- True if water should fit to the terrain it is drawn over, false if it goes through.
	options.fitToTerrain = heightFitTerrain;
	
	-- Location of the waypoint.
	options.x1 = 200
	options.y1 = 200
	options.h = 20	
	
	-- Display the location.
	options.highlight = Highlighter.Create(nil, 0)
	return options
end

-- Stores the current river options to set/display.
currentRiverOptions = Waypoint.Create()

-- Create a deep copy of a table.
-- Copies the elements.
function CopyTable(object)
    local holdTable = {}
    local function Copy(object)
        if type(object) ~= "table" then
            return object
        elseif holdTable[object] then
            return holdTable[object]
        end
        local clone = {}
        holdTable[object] = clone
        for index, value in pairs(object) do
            clone[Copy(index)] = Copy(value)
        end
        return setmetatable(clone, getmetatable(object))
    end
    return Copy(object)
end

-- Creates a copy of waypoint data.  Waypoint data is copied, but a new highlighter is created.
-- Return:
-- Value is a new waypoint with the old waypoint data.
function Waypoint:Clone(ID)
	clonedObj = CopyTable(self)

	-- Add in a new highlight, since we don't want that shared.
	clonedObj.highlight = Highlighter.Create(nil, ID)
	return clonedObj
end

-- Set the position of the waypoint.
-- Updates highlighting as needed.
-- x, y, z - Cell position to put the waypoint at.
function Waypoint:SetPosition(x, y, z)
	self.x1 = x
	self.h = y
	self.z1 = z
	self.highlight:UpdatePosition(Vector3.new(x, FindMaterialDown(x, y, z), z))
end

-- Stores the last continuous joint type made.
lastDrawJoint = noChange

-- Y position of the last river section that was drawn.
-- Used for continuing a river.
lastRiverY = 0

-- Which draw mode is in use.
drawMode = drawImmediate

-- Weither water dropping/going up should be allowed.
allowWaterfalls = true

-- Store waypoint related things.
waypointValues = {nextWaypointID = 1, 	-- Next waypoint ID to use.
                  waypoints = {}}		-- This will hold all waypoints used to draw a river.  The first entry is the start of the river.  Each subsequent one is the endpoint of a segment.  
---------------
--PLUGIN SETUP-
---------------
plugin = PluginManager():CreatePlugin()
mouse = plugin:GetMouse()

if allowClicking then
	mouse.Button1Down:connect(function() onClicked(mouse) end)
end

plugin.Deactivation:connect(function()
	Off()
end)
toolbar = plugin:CreateToolbar("Water")
toolbarbutton = toolbar:CreateButton("", "Create a river", "river.png")
toolbarbutton.Click:connect(function()
	if on then
		Off()
	elseif loaded then
		On()
	end
end)

-- Create the mouse movement highlighter.
mouseHighlighter = MouseHighlighter.Create(mouse)

------
--GUI-
------
--screengui
g = Instance.new("ScreenGui", game:GetService("CoreGui"))
g.Name = 'RiverGui'

-- UI gui load.  Required for sliders.
local RbxGui = LoadLibrary("RbxGui")

-- Gui frame for river options.
riverOptionsDragBar, riverOptionsFrame, riverOptionsHelpFrame, riverOptionsCloseEvent = RbxGui.CreatePluginFrame("River Creator",UDim2.new(0,163,0,180),UDim2.new(0,0,0,0),false,g)
riverOptionsDragBar.Visible = false
riverOptionsCloseEvent.Event:connect(function (  )
	Off()
end)

riverOptionsHelpFrame.Size = UDim2.new(0,300,0,180)

local riverOptionsHelpText = Instance.new("TextLabel",riverOptionsHelpFrame)
riverOptionsHelpText.Name = "HelpText"
riverOptionsHelpText.Font = Enum.Font.ArialBold
riverOptionsHelpText.FontSize = Enum.FontSize.Size12
riverOptionsHelpText.TextColor3 = Color3.new(227/255,227/255,227/255)
riverOptionsHelpText.TextXAlignment = Enum.TextXAlignment.Left
riverOptionsHelpText.TextYAlignment = Enum.TextYAlignment.Top
riverOptionsHelpText.Position = UDim2.new(0,4,0,4)
riverOptionsHelpText.Size = UDim2.new(1,-8,0,177)
riverOptionsHelpText.BackgroundTransparency = 1
riverOptionsHelpText.TextWrap = true
riverOptionsHelpText.Text = [[
Creates rivers thru a path that is dragged by the mouse.  Simply hold the left mouse button down and drag the river out.

River Force: 
How fast water flows.

Tunnel Height: 
When Tunneling is enabled, this is how high above water to remove terrain, in blocks.
Only used for Draw Height: Static

River Width:
How wide the river is, in blocks.

River Depth:
How deep the water will be, in blocks.]]

local linkImage = Instance.new("ImageLabel",riverOptionsFrame)
linkImage.Name = "LinkImage"
linkImage.BackgroundTransparency = 1
linkImage.Visible = false
linkImage.Size = UDim2.new(0,15,0,15)
linkImage.Position = UDim2.new(0,120,0,51)
game:GetService("ContentProvider"):Preload("http://gametest.roblox.com/asset/?id=129724452")
linkImage.Image = "http://gametest.roblox.com/asset/?id=129724452" --todo: put this in a better spot

-- Create a standard text label.  Use this for all lables in the popup so it is easy to standardize.
-- labelName - What to set the text label name as.
-- pos    	 - Where to position the label.  Should be of type UDim2.
-- size   	 - How large to make the label.	 Should be of type UDim2.
-- text   	 - Text to display.
-- parent 	 - What to set the text parent as.
-- Return:
-- Value is the created label.
function CreateStandardLabel(labelName,
                             pos,
							 size,
							 text,
							 parent)
	local label = Instance.new("TextLabel", parent)
	label.Name = labelName
	label.Position = pos
	label.Size = size
	label.Text = text
	label.TextColor3 = Color3.new(0.95, 0.95, 0.95)
	label.Font = Enum.Font.ArialBold
	label.FontSize = Enum.FontSize.Size14
	label.TextXAlignment = Enum.TextXAlignment.Left
	label.BackgroundTransparency = 1	
	label.Parent = parent	
	
	return label
end

-- Create a standardized slider.
-- name  - Name to use for the slider.
-- pos   - Position to display the slider at.
-- steps - How many steps there are in the slider.
-- funcOnChange - Function to call when the slider changes.
-- initValue    - Initial value to set the slider to.  If nil the slider stays at the default. 
-- parent       - What to set as the parent.
-- Return:
-- sliderGui      - Slider gui object.
-- sliderPosition - Object that can set the slider value.
function CreateStandardSlider(name,
                              pos,
							  lengthBarPos,
							  steps,
							  funcOnChange,
							  initValue,
							  parent)
	local sliderGui, sliderPosition = RbxGui.CreateSlider(steps, 0, UDim2.new(0,0,0,0))
	
	sliderGui.Name = name
	sliderGui.Parent = parent
	sliderGui.Position = pos
	sliderGui.Size = UDim2.new(1,0,0,20)
	local lengthBar = sliderGui:FindFirstChild("Bar")
	lengthBar.Size = UDim2.new(1, -21, 0, 5)
	lengthBar.Position = lengthBarPos
	
	if nil ~= funcOnChange then
		sliderPosition.Changed:connect(function() funcOnChange(sliderPosition) end)
	end
	
	if nil ~= initValue then 
		sliderPosition.Value = initValue
	end
	
	return sliderGui, sliderPosition 
end

-- Create a standard dropdown.  Use this for all dropdowns in the popup so it is easy to standardize.
-- name - What to set the text label name as.
-- pos    	 	 - Where to position the label.  Should be of type UDim2.
-- values    	 - A table of the values that will be in the dropbox, in the order they are to be shown.
-- initValue	 - Initial value the dropdown should be set to.
-- funcOnChange  - Function to run when a dropdown selection is made.
-- parent 	 	 - What to set the parent as.
-- Return:
-- dropdown 	   - The dropdown gui.
-- updateSelection - Object to use to change the current dropdown.																																		
function CreateStandardDropdown(name,
						        pos,
								values,
								initValue,
								funcOnChange,
								parent)					
	-- Create a dropdown selection for the modes to fill in a river
	local dropdown, updateSelection=RbxGui.CreateDropDownMenu(values, funcOnChange);
	dropdown.Name = name
	dropdown.Position = pos
	dropdown.Active = true
	dropdown.Size = UDim2.new(0,150,0,32)
	dropdown.Parent = parent

	updateSelection(initValue)		

	return dropdown, updateSelection
end		

-- Create a standard checkbox.  Use this for all checkboxes in the popup so it is easy to standardize.
-- name - What to use.
-- pos    	 	- Where to position the button.  Should be of type UDim2.
-- funcOnPress  - Function to run when the button is pressed.  This should return a bool, true if the checkbox should be checked and false if not.
-- isChecked    - True if the checkbox should be checked initially, false if not.
-- parent 	 	- What to set the parent as.
-- Return:
-- button 	   - The button gui.					
function CreateStandardCheckbox(name,
  							    pos,
							    funcOnPress,
								isChecked,
							    parent)										
	local checkbox = Instance.new("TextButton", parent)
	checkbox.Name = name
	checkbox.Style = Enum.ButtonStyle.RobloxButton
	checkbox.Size = UDim2.new(0,32,0,32)
	checkbox.Position = pos --UDim2.new(0,-34,0.5,-16)

	if isChecked then
		checkbox.Text = "X"
	else
		checkbox.Text = ""
	end	
	
	checkbox.Font = Enum.Font.ArialBold
	checkbox.FontSize = Enum.FontSize.Size18
	checkbox.TextColor3 = Color3.new(1,1,1)
	checkbox.MouseButton1Click:connect(function()
											local checked = funcOnPress()
											if checked then
												checkbox.Text = "X"
											else
												checkbox.Text = ""
											end
										end)
	return checkbox
end	

-- Keep common button properties here to make it easer to change them all at once.
-- These are the default properties to use for a button.
buttonTextColor = Color3.new(1, 1, 1);
buttonFont = Enum.Font.ArialBold;
buttonFontSize = Enum.FontSize.Size18;

-- Create a standard dropdown.  Use this for all dropdowns in the popup so it is easy to standardize.
-- name - What to use.
-- pos    	 	- Where to position the button.  Should be of type UDim2.
-- text         - Text to show in the button.
-- funcOnPress  - Function to run when the button is pressed.
-- parent 	 	- What to set the parent as.
-- Return:
-- button 	   - The button gui.																																	
function CreateStandardButton(name,
  							  pos,
							  text,
							  funcOnPress,
							  parent)					
	button = Instance.new("TextButton", parent)
	button.Name = name
	button.Position = pos

	button.Size = UDim2.new(0,150,0,40)
	button.Text = text
	
	button.Style = Enum.ButtonStyle.RobloxButton

	button.TextColor3 = buttonTextColor
	button.Font = buttonFont
	button.FontSize = buttonFontSize

	button.MouseButton1Click:connect(funcOnPress)
	
	return button
end	

-- Change the current fill mode.
-- mode - Fill mode string ID to use.
function ChangeFillMode(mode)
	currentRiverOptions.fillRiverMode = mode
end

-- Change the current mode that specifies what can be turned into water..
-- mode - Water mode string ID to use.
function ChangeWaterMode(mode)
	currentRiverOptions.changeToWaterMode = mode
end

-- Change river flow direction.
-- direction - The direction or method of determining the direction to use when creating a river.
function ChangeFlowDirection(direction)
	currentRiverOptions.riverDirection = direction
end

-- Get the text to display for the current river width.
function GetRiverWidthText()
	return 'River Width: ' .. currentRiverOptions.riverWidth
end

-- Get the text to display for the current river thickness.
function GetRiverThicknessText()
	return 'River Depth: ' .. currentRiverOptions.riverThickness
end

-- Get the text to display for the current tunnel height.
function GetTunnelHeightText()
	return 'Tunnel Height: ' .. currentRiverOptions.tunnelHeight
end

-- Get the text to display for the current number of steps in a river section.
function GetRiverStepsText()
	return 'Curviness: ' .. currentRiverOptions.riverSteps
end

-- Handle turning on fitting to terrain.  Some options may need to be disabled or enabled, and values set.
function FitTerrainOn()
	updateFillModeSelection(fillModeDown)
	currentRiverOptions.fillAboveRiver = false
	tunnelingLabel.Visible = false
	tunnelCheckbox.Visible = false
	fillModeDropdownLabel.Visible = false
	fillModeDropdown.Visible = false
end

-- Handle turning on fitting to terrain.  Some options may need to be disabled or enabled, and values set.
function FitTerrainOff()
	tunnelingLabel.Visible = true
	tunnelCheckbox.Visible = true
	fillModeDropdownLabel.Visible = true
	fillModeDropdown.Visible = true
end

-- Set how water fits to terrain.
-- Can disable/enable properties that shouldn't be used with the corresponding mode.
function ChangeFitTerrain(value)
	currentRiverOptions.fitToTerrain = value
	
	-- C5 Start Change
	-- Allow tunneling stuff to still be on.
	--[[
	if currentRiverOptions.fitToTerrain == heightFitTerrain then
		FitTerrainOn()
	elseif currentRiverOptions.fitToTerrain == heightStatic then
		FitTerrainOff()
	end
	--]]
	-- C5 End Change
end		

-- Toggle tunneling/digging above the river on/off.
function ToggleTunnel()
	currentRiverOptions.fillAboveRiver = not currentRiverOptions.fillAboveRiver
	return currentRiverOptions.fillAboveRiver
end
			
-- Stops a river or river section currently being created.
-- If it is using waypoints, it will clear the waypoints.
-- Sets the next click to be the start of a new river/river section.
function EndCurrentRiver()	
	-- Reset waypoints
	ClearWaypoints()
	mouseHighlighter.lastUsedPoint = nil
	currentRiverOptions.startRiver = true
	lastDrawJoint = noChange
	UnloadProgressBar()
end

-- Wipe out the waypoints.
function ClearWaypoints()
	if 0 ~= #waypointValues.waypoints then
		currentRiverOptions = waypointValues.waypoints[#waypointValues.waypoints]
	end
	
	-- Waypoints no longer needed.
	for ind = 1, #waypointValues.waypoints, 1 do
		waypointValues.waypoints[ind].highlight:DisablePreview()
		waypointValues.waypoints[ind] = nil
	end

	waypointValues.waypoints = {}		
	
	waypointValues.nextWaypointID = 1
end

-- Slider for controlling the river force, how strong the river is flowing.
riverForceSliderLabel = CreateStandardLabel("RiverForceSliderLabel", UDim2.new(0, 8, 0, 10), UDim2.new(0, 67, 0, 14), "", riverOptionsFrame)
riverForceSliderGui, riverForceSliderPosition = CreateStandardSlider('RiverForceSliderGui', UDim2.new(0,1,0,26), UDim2.new(0,10,0.5,-2), #FORCE_NAMES, 
																	function(riverForceSliderPosition) 
																		currentRiverOptions.riverForce = riverForceSliderPosition.Value - 1
																		riverForceSliderLabel.Text = 'River Force: ' .. FORCE_NAMES[riverForceSliderPosition.Value]
																	end, currentRiverOptions.riverForce + 1, riverOptionsFrame)
							
-- Slider for controlling the river force, how strong the river is flowing.
tunnelHeightSliderLabel = CreateStandardLabel("TunnelHeightSliderLabel", UDim2.new(0, 8, 0, 51), UDim2.new(0, 59, 0, 14), "", riverOptionsFrame)
tunnelHeightSliderGui, tunnelHeightSliderPosition = CreateStandardSlider('tunnelHeightSliderGui', UDim2.new(0,1,0,67), UDim2.new(0,10,0.5,-2), math.floor(maxTunnelHeight/2 + 1), 
																	function(tunnelHeightSliderPosition)
																		-- Convert slider value to a odd number
																		currentRiverOptions.tunnelHeight = ((tunnelHeightSliderPosition.Value - 1) * 2) + 1
																		tunnelHeightSliderLabel.Text = GetTunnelHeightText()
																	end, math.floor(currentRiverOptions.tunnelHeight/2 + 1), riverOptionsFrame)																	
																
-- Slider for controlling how wide the river is.
riverWidthSliderLabel = CreateStandardLabel("RiverWidthSliderLabel", UDim2.new(0, 8, 0, 92), UDim2.new(0, 51, 0, 14), "", riverOptionsFrame)
riverWidthSliderGui, riverWidthSliderPosition = CreateStandardSlider('RiverWidthSliderGui', UDim2.new(0,1,0,108), UDim2.new(0,10,0.5,-2), math.floor(maxRiverWidth/2 + 1), 
																	function(riverWidthSliderPosition)
																		currentRiverOptions.riverWidth = ((riverWidthSliderPosition.Value - 1) * 2) + 1	-- Convert slider value to a odd number
																		riverWidthSliderLabel.Text = GetRiverWidthText()
																	end, math.floor(currentRiverOptions.riverWidth/2 + 1), riverOptionsFrame)		
																	
-- Slider for controlling how wide the river is.
riverThicknessSliderLabel = CreateStandardLabel("RiverThicknessSliderLabel", UDim2.new(0, 8, 0, 133), UDim2.new(0, 43, 0, 14), "", riverOptionsFrame)
riverThicknessSliderGui, riverThicknessSliderPosition = CreateStandardSlider('RiverThicknessSliderGui', UDim2.new(0,1,0,149), UDim2.new(0,10,0.5,-2), math.floor(maxRiverThickness/2 + 1), 
																	function(riverThicknessSliderPosition)
																		currentRiverOptions.riverThickness = ((riverThicknessSliderPosition.Value - 1) * 2) + 1	-- Convert slider value to a odd number
																		riverThicknessSliderLabel.Text = GetRiverThicknessText()
																	end, math.floor(currentRiverOptions.riverThickness/2 + 1), riverOptionsFrame)																			
																																													
-- Load the progress bar to display when drawing a river.
-- steps - How many steps of progress there are in the bar.  This value will mean 100 percent done.
function LoadProgressBar(steps)
	if cancelValues.progressBar == nil then
		cancelValues.isDrawing = true
		
		-- Start the progress bar.
		cancelValues.progressBar, cancelValues.setAmountFunc, cancelValues.bindForCancel = RbxGui.CreateLoadingFrame('ProgressBar')
		cancelValues.progressBar.Position = UDim2.new(-100,0,0,0)
		cancelValues.progressBar.Parent = g

		cancelValues.bindForCancel.Event:connect(function(arguments)
			cancelValues.cancelActions = true		-- Set the flag that everything should stop.
			print('Hit cancel')
			
			coroutine.yield()
			EndCurrentRiver()
		end)
	else
		print('Tried to start the progress bar when it was already running.')
	end
end

-- Unload the progress bar.
function UnloadProgressBar()
	cancelValues.isDrawing = false
	cancelValues.cancelActions = false
	
	if nil ~= cancelValues.progressBar then
		cancelValues.progressBar.Parent = nil
		cancelValues.progressBar = nil
	end
end

function coordCheck(x, z, height)
	c = game.Workspace.Terrain
	for h = height, 0, -1 do
		material, wedge, rotation = GetCell(c, x, h, z)
		if material.Value > 0 then
			return true
		elseif height == 0 then
			return false
		end
	end
end

-- Each river section can be thought of as two parts.  This will tell which direction each part will go.
-- x1, x2 - Start and end x positions.
-- z1, z2 - Start and end z positions. 
-- Return:
-- Returns two values.  The first is the x direction that will be drawn.
-- The second is the z direction that will be drawn.
function RiverDirections(x1, x2, z1, z2)
	currJointX = noChange
	currJointZ = noChange
	
	if x1 > x2 then
		currJointX = decreaseX
	elseif x1 < x2 then
		currJointX = increaseX		
	end
	
	if z1 > z2 then
		currJointZ = decreaseZ
	elseif z1 < z2 then
		currJointZ = increaseZ				
	end
	
	return currJointX, currJointZ
end

-- Starting at the given coord, look for a material that isn't 0.
-- Goes down in the y direction to look.
-- x, y z - Location to start checking.
-- Return:
-- hitY - First y from the location that a material was encountered at.
-- hit - Wheither a hit occured. True for a hit, false for a miss.
function FindMaterialDown(x, y, z)
	c = game.Workspace.Terrain
	for h = y, 0, -1 do
		material, wedge, rotation = GetCell(c, x, h, z)	
		
		if 0 ~= material.Value then
				return h, true
		elseif h == 0 then
			return y, false
		end	
	end
	return y, false
end

-- Starting at the given coord, look for a material that isn't 0.
-- Goes down in the y direction to look.
-- x, y z - Location to start checking.
-- Return:
-- hitY - First y from the location that a material was encountered at.
-- hit - Wheither a hit occured. True for a hit, false for a miss.
function FindMaterialDownIgnoreWater(x, y, z)
	c = game.Workspace.Terrain
	for h = y, 0, -1 do
		material, wedge, rotation = GetCell(c, x, h, z)	
		
		if 0 ~= material.Value and waterMaterialID ~= material.Value then
				return h, true
		elseif h == 0 then
			return y, false
		end	
	end
	
	return y, false
end

-- Starting at the given coord, look for a material that isn't 0 or water (17).
-- If found, it tries to return the position based on the current settings, that would have drawn water at that spot.
-- Use so that water doesn't always get drawn on top of water.
-- Goes down in the y direction to look.
-- x, y z - Location to start checking.
-- Return:
-- hitY - First y from the location that a material was encountered at.
-- hit - Wheither a hit occured. True for a hit, false for a miss.
--[[
function FindMaterialDownUseWater(x, y, z)
	c = game.Workspace.Terrain
	for h = y, 0, -1 do
		material, wedge, rotation = GetCell(c, x, h, z)	
		
		if (0 ~= material.Value) and (waterMaterialID ~= material.Value) then
				for upward = 1, currentRiverOptions.riverThickness, 1 do
					material, wedge, rotation = GetCell(c, x, h + upward, z)	
					if waterMaterialID ~= material.Value then
						return h + upward, true
					end
				end
				
				return h + currentRiverOptions.riverThickness, true
		end
		
		if h == 0 then
			return currentRiverOptions.riverThickness, false
		end	
	end
	
	return y, false
end
--]]
-- This is the newer way, but it still messes up.  It has to be something along this line...

function FindMaterialDownUseWater(x, y, z)
	c = game.Workspace.Terrain
	for h = y, 0, -1 do
		material, wedge, rotation = GetCell(c, x, h, z)	
		
		if (0 ~= material.Value) and (waterMaterialID ~= material.Value) then
				for upward = 1, currentRiverOptions.riverThickness, 1 do
					material, wedge, rotation = GetCell(c, x, h + upward, z)	
					if waterMaterialID ~= material.Value then
						material, wedge, rotation = GetCell(c, x, h + upward + 1, z)	
						if waterMaterialID ~= material.Value then
							return h
						end

						if h ~= y then
							return h + upward, true
						else
							return y, true
						end
					end
				end
				
				if h ~= y then
					return h + currentRiverOptions.riverThickness, true
				end
		end
		
		if h == 0 then
			return currentRiverOptions.riverThickness, false
		end	
	end
		
	return y, false
end


-- This will store a table of tables.
-- Each entry in [][inds] will be a point.
waterfallPoints = nil

-- Attempt to create a one block wide waterfaell.
-- x, y, z - Waterfall start.  Should be the top of the waterfall.
-- endY    - The base of the waterfall.
-- fromDirection - Which way water came to the waterfall.
function CreateWaterfall(x, y, z, endY, fromDirection)
	-- No upward waterfalls.
	-- If there is a upward fall, don't create anything.
	-- Always return the lowest point.
	if endY >= y then
		return y
	end
	
	-- Do not draw if waterfalls are off.
	if allowWaterfalls then
		--[[
		print('Waterfall y ' .. y)
		print('Waterfall endY ' .. endY)
		--]]
		if endY ~= y then
			FillWaterColumn(x, y, z, endY, fromDirection)
			
			-- Depending on how waterfalls should flow, change the direction at the top or bottom to push it along.
			if endY >= y then
				SetWaterCell(c, x, endY, z, currentRiverOptions.riverForce, fromDirection)
			end
		end
	end
	
	if y > endY then
	--	print('Last y should be endY ' .. endY)
		return endY
	else
	--	print('Last y should be y ' .. y)
		return y
	end	
end

-- Fill in water in a column.  Fills up or down, depending on the given values.
-- x, y, z - Start of the column.
-- endY - End y location to draw the piller to.  
-- Return:
-- Value is the last position filled.
function FillWaterColumn(x, y, z, endY, fromDirection)
	--print('Column ' .. y .. ' ' .. endY)
	
	-- If the waterfall was drawn from bottom to top, then store all the top points.
	-- If it was drawn from top to bottom, store all the bottom points.
	
	local change = 0	
	if endY > y then
		change = 1
	else
		change = -1
	end
		
	local direction = currentRiverOptions.waterfallDirection
	if flowStartToEnd == currentRiverOptions.waterfallDirection then
		if endY > y then
			direction = flowPositiveY
		else
			direction = flowNegativeY
		end
	elseif flowEndToStart == currentRiverOptions.waterfallDirection then
		if endY < y then
			direction = flowPositiveY
		else
			direction = flowNegativeY
		end
	end
	
	if nil ~= fromDirection then
		
		if 1 <= (math.abs(math.abs(y) - math.abs(endY))) then
			direction = WaterDirectionIDToName(fromDirection)
		end
		
	end
	
	if y ~= endY then
		for nextY = y, endY, change do
			SetWaterCell(c, x, nextY, z, currentRiverOptions.riverForce, WaterDirectionToID(direction))
		end
	end	
end

-- Precalculate points for generating a slice of the river.
-- A slice is the width of the river and one block in the direction the river would be drawn.
function PrecalcRiverPoints(startX, h, startZ, 
						    changeX, changeZ,
                            prevEndYs,
							fillWaterOffsetY,
							attemptWaterfall)	
	halfWidth = math.floor(currentRiverOptions.riverWidth/2)
		
	fitTerrain = (heightFitTerrain == currentRiverOptions.fitToTerrain)	
		
	-- For testing.
	--[[
	print('------------------')
	print('PrecalcRiverPoints')
	print('startX ' .. startX)
	print('h ' .. h)
	print('startZ ' .. startZ)
	print('prevEndYs')
	for ind = 1, #prevEndYs, 1 do
		print(prevEndYs[ind])
	end
	--]]
	
							
	-- Will store a table of y values, one for each point.
	-- This is needed for waterfalls, so that it knows it needs to draw one.
	local baseYs = {}		-- Base of the waterfall.  For a downward waterfall, this would be the lowest point.
	local startYs = {}
	local highestRiverBlocks = {}

	for ind = 1, #prevEndYs, 1 do
		startYs[ind] = prevEndYs[ind]
	end
	
	-- I preprocess their start positions and drop position.	
	-- This will draw river in the z direction.
	if changeX then
		for drawDepth = 0, currentRiverOptions.riverThickness-1, 1 do	
			for drawZ = -halfWidth, halfWidth, 1 do
				currY = h
				
				-- Only do this for fitting to terrain.
				if fitTerrain then			
					currY = FindMaterialDown(startX, h, startZ + drawZ)
				end
				
				-- Precompute all the start and drop down end points for the current slice of the river.
				if attemptWaterfall then
					baseYs[halfWidth + drawZ + 1], found = FindMaterialDown( startX, currY, startZ + drawZ)
					--baseYs[halfWidth + drawZ + 1], found = FindMaterialDownUseWater( startX, currY, startZ + drawZ)
					if not found then
						baseYs[halfWidth + drawZ + 1] = currY
					end
				else
					baseYs[halfWidth + drawZ + 1] = currY
				end
					
				highestRiverBlocks[halfWidth + drawZ + 1] = baseYs[halfWidth + drawZ + 1] + (currentRiverOptions.riverThickness-1) + fillWaterOffsetY
			end
		end
	elseif changeZ then
		for drawDepth = 0, currentRiverOptions.riverThickness-1, 1 do	
			for drawX = -halfWidth, halfWidth, 1 do
				currY = h
				
				-- Only do this for fitting to terrain.
				if fitTerrain then
					currY = FindMaterialDown( startX + drawX, h, startZ)
				end
				
				-- Precompute all the start and drop down end points for the current slice of the river.
				if attemptWaterfall then
					baseYs[halfWidth + drawX + 1], found = FindMaterialDown( startX + drawX, currY, startZ)
					--baseYs[halfWidth + drawX + 1], found = FindMaterialDownUseWater( startX + drawX, currY, startZ)
					if not found then
						baseYs[halfWidth + drawX + 1] = currY
					end
				else
					baseYs[halfWidth + drawX + 1] = currY
				end
					
				highestRiverBlocks[halfWidth + drawX + 1] = baseYs[halfWidth + drawX + 1] + (currentRiverOptions.riverThickness-1) + fillWaterOffsetY
			end
		end	
	end

	-- For testing.
	--[[
	print('-----------')
	print('End precalc')
	print('startYs')
	for ind = 1, #startYs, 1 do
		print(startYs[ind])
	end
	for ind = 1, #baseYs, 1 do
		print(baseYs[ind])
	end
	--]]
	
	return startYs, baseYs, highestRiverBlocks
end

--Create a path between coordinates (x1,z1) and (x2,z2) at height h in terrain object.
-- Builds a river from the start to the end point.  river does not move diagonally.
-- Can draw struts for eleveated rivers and hollow out above the path, based on settings.
--if you want path to come from x direction, make it start at the place
--if you want it to come from z direction, make it end at the place
-- continuation  - True if the section is part of a currently being drawn river, false if it will be the start.
-- continuationY - If the path is a continuation, this will be the last y value used.
-- w1, w2        - The waypoints to draw the section between.  The start is w1 and the end is w2
-- Return:
-- Value is the y position of the center of the last slice of river drawn.
function makePath(x1, z1, x2, z2, h,
                  continuation, 
				  continuationY,
				  w1, w2)
	-- For testing.
	--[[
	print(' ')
	print('Start of makePath')
	print('x1 ' .. x1 .. ' x2 ' .. x2 .. ' z1 ' .. z1 .. ' z2 ' .. z2)
	print('Continuation y ' .. continuationY)
	--]]
	
	local fitTerrain = (heightFitTerrain == w1.fitToTerrain)
	
	-- So we know which directions actually got drawn.
	local changeX = x1 ~= x2
	local changeZ = z1 ~= z2

	-- The first spot drawn should never have a waterfall.
	local ignoreFirstWaterfall = not continuation

	local waterfallCount = 1

	-- Needed for drawing rivers, lining them up.
	-- The decimal part isn't needed.
	local halfWidth = math.floor(w1.riverWidth/2)
	
	-- Determine what the last draw direction will be. Needed for joining sections.
	-- It is drawing horizontal.
	-- It will need to adjust the start point for continuous if the new start is perpendicular to the old end.
	if lastDrawJoint ~= noChange then
		currJointX, currJointZ = RiverDirections(x1, x2, z1, z2)
		
		-- Adjust the start of the river section.
		-- river section joins need to be adjusted for 90 degree changes.
		-- Depending on the draw directions, adjust the start river draw point.
		if decreaseZ == lastDrawJoint or increaseZ == lastDrawJoint then
			if decreaseX == currJointX then
				x1 = x1 + halfWidth
			elseif increaseX == currJointX then
				x1 = x1 - halfWidth
			end
		end
	end
	
	if noChange ~= currJointZ then
		lastDrawJoint = currJointZ
	elseif noChange ~= currJointZ then
		lastDrawJoint = currJointZ	
	end

	if x2 < x1 then
		incx = -1
	else 
		incx = 1
	end

	-- Determine were water should flow.
	local flowDirectionX = DetermineDirX(x1, x2, w1.riverDirection)
	local flowDirectionZ = DetermineDirZ(z1, z2, w1.riverDirection)
		
	-- Move the opposite way drawn.
	if flowEndToStart == w1.riverDirection then
		flowDirectionX = WaterDirectionToID(ReverseFlow(WaterDirectionIDToName(flowDirectionX)))
		flowDirectionZ = WaterDirectionToID(ReverseFlow(WaterDirectionIDToName(flowDirectionZ)))
	end
		
	if (flowStartToEnd == w1.riverDirection) or (flowEndToStart == w1.riverDirection ) then	
		-- Handler water not flowing in a direction.
		if nil == flowDirectionX and nil ~= flowDirectionZ then
			flowDirectionX = flowDirectionZ
		elseif nil == flowDirectionZ and nil ~= flowDirectionX then
			flowDirectionZ = flowDirectionX
		elseif nil == flowDirectionZ and nil == flowDirectionZ then
			-- Water must flow.
			flowDirectionX = 1
			flowDirectionZ = 1
		end
	else
		flowDirectionX = WaterDirectionToID(w1.riverDirection)
		flowDirectionZ = WaterDirectionToID(w1.riverDirection)
	end
	
	-- Depending on the fill mode, determine what direction water should fill.
	local fillWaterOffsetY = 0
	if fillModeUp == w1.fillRiverMode then
		fillWaterOffsetY = 0
	elseif fillModeDown == w1.fillRiverMode then
		fillWaterOffsetY = -(w1.riverThickness-1)
	elseif fillModeCentered == w1.fillRiverMode then
		fillWaterOffsetY = -math.floor((w1.riverThickness)/2)
	else
		print('Error, invalid fill mode ' .. w1.fillRiverMode)
	end	

	-- Will store a table of y values, one for each point.
	-- This is needed for waterfalls, so that it knows it needs to draw one.
	local startYs = {}
	local endYs = {}
	local highestRiverBlocks = {}
	
	for preset = 1, w1.riverWidth, 1 do
		startYs[preset] = h
		
		if continuation then
			endYs[preset] = continuationY
		else
			endYs[preset] = h
		end
	end
	
	-- Store what to use as the final y position for drawing in the x and z directions.
	-- If a direction is actually drawn.  The last direction actually drawn will be the one that gets returned as the contuation y position.
	local endYForX = endYs[halfWidth+1]
	local endYForZ = endYs[halfWidth+1]
	
	for x = x1, x2, incx do
		if cancelValues.cancelActions then
			return
		end

		-- For testing.
		--[[
		print()
		print('precalc')
		--]]
		
		-- Precalculate the points so that as the river gets drawn it doesn't mess with determining what to do next.
		startYs, endYs, highestRiverBlocks = PrecalcRiverPoints(x, h, z1, true, false, endYs, fillWaterOffsetY, fitTerrain)
		endYForX = endYs[halfWidth+1]
		
		-- For testing.
		--[[
		print('startYs')
		for ind = 1, #startYs, 1 do
			print(startYs[ind])
		end
		print('endYs')
		for ind = 1, #endYs, 1 do
			print(endYs[ind])
		end
		print('endYForX ' .. endYForX)
		--]]
		
		-- This will draw river in the z direction.
		for drawDepth = 0, w1.riverThickness-1, 1 do
			HandleDrawYield()
			
			for drawZ = -halfWidth, halfWidth, 1 do
				-- For fitting to terrain, it has to drop down/move up.
				if fitTerrain and not ignoreFirstWaterfall then
					if endYs[drawZ + halfWidth + 1] ~= startYs[drawZ + halfWidth + 1] then
						waterfallCount = waterfallCount + 1
						
						-- CreateWaterfall(x, startYs[drawZ + halfWidth + 1], z1 + drawZ, endYs[drawZ + halfWidth + 1], flowDirectionX)
						local holdY = CreateWaterfall(x, startYs[drawZ + halfWidth + 1], z1 + drawZ, endYs[drawZ + halfWidth + 1], flowDirectionX)
						if holdY < h then 
							h = holdY
						end

						ignoreFirstWaterfall = false
					end	
				end
				
				-- Only try to draw if it actually moved in that direction
				if changeX then
					-- If it is the mode where everything gets changes to water, then change to water.
					if w1.changeToWaterMode  == changeTerrainAir then
						SetWaterCell(c, x, endYs[drawZ + halfWidth + 1] + drawDepth + fillWaterOffsetY, z1 + drawZ, w1.riverForce, flowDirectionX)
					else
						-- Determine if the material type is allowed to change and change as needed.
						local holdMaterial = GetCell(c, x, endYs[drawZ + halfWidth + 1] + drawDepth + fillWaterOffsetY, z1 + drawZ).Value
						if changeTerrain == w1.changeToWaterMode and 0 ~= holdMaterial then
							SetWaterCell(c, x, endYs[drawZ + halfWidth + 1] + drawDepth + fillWaterOffsetY, z1 + drawZ, w1.riverForce, flowDirectionX)
						elseif changeAir == w1.changeToWaterMode and 0 == holdMaterial then
							SetWaterCell(c, x, endYs[drawZ + halfWidth + 1] + drawDepth + fillWaterOffsetY, z1 + drawZ, w1.riverForce, flowDirectionX)	
						end
					end
				end
			end
		end
		
		-- This will fill in above the river in the z direction.
		if (w1.fillAboveRiver and changeX) then
			for fillHeight = 1, w1.tunnelHeight, 1 do	
				HandleDrawYield()
				for drawZ = -halfWidth, halfWidth, 1 do	
					-- Don't remove water.
					-- Only change to air if a water cell had been drawn above.
					if waterMaterialID == GetCell(c, x, highestRiverBlocks[drawZ + halfWidth + 1], z1 + drawZ).Value then
						if waterMaterialID ~= GetCell(c, x, highestRiverBlocks[drawZ + halfWidth + 1] + fillHeight, z1 + drawZ).Value then
							SetCell(c, x, highestRiverBlocks[drawZ + halfWidth + 1] + fillHeight, z1 + drawZ, w1.tunnelMaterial, 0, 0)
						end
					end
				end
			end		
		end
	end
	
	if z2 < z1 then
		incz = -1
	else
		incz = 1
	end
	
	-- Adjust the join point so it isn't jagged.
	if x1 ~= x2 then
		if z1 < z2 then
			z1 = z1 - halfWidth
		elseif z1 > z2 then
			z1 = z1 + halfWidth
		end	
	end
	continuation = true
	-- Adjust for join.
	if (currJointZ == increaseZ or currJointZ == decreaseZ) and currJointX == noChange then
		z1 = z1 + 1
	end
	
	-- This is for the x direction.
	for z = z1, z2, incz do
		HandleDrawYield()
		if cancelValues.cancelActions then
			return
		end	
	
		-- Precalculate the points so that as the river gets drawn it doesn't mess with determining what to next.
		
		for preset = 1, w1.riverWidth, 1 do
			endYs[preset] = endYs[halfWidth + 1]
		end		
		
		startYs, endYs, highestRiverBlocks = PrecalcRiverPoints(x2, h, z, true, false, endYs, fillWaterOffsetY, fitTerrain)
		endYForZ = endYs[halfWidth+1]
		
		-- This will draw in the x direction.
		for drawDepth = 0, w1.riverThickness-1, 1 do
			HandleDrawYield()
			for drawX = -halfWidth, halfWidth, 1 do		
				-- For fitting to terrain, it has to drop down/move up.
				if fitTerrain and not ignoreFirstWaterfall then
					if endYs[drawX + halfWidth + 1] ~= startYs[drawX + halfWidth + 1] then
						-- CreateWaterfall(x2 + drawX, startYs[drawX + halfWidth + 1], z, endYs[drawX + halfWidth + 1], flowDirectionZ)
						local holdY = CreateWaterfall(x2 + drawX, startYs[drawX + halfWidth + 1], z, endYs[drawX + halfWidth + 1], flowDirectionZ)
						if holdY < h then
							h = holdY
						end

						ignoreFirstWaterfall = false
					end	
				end
				
				if changeZ then
					if w1.changeToWaterMode  == changeTerrainAir then
						SetWaterCell(c, x2 + drawX, endYs[drawX + halfWidth + 1] + drawDepth + fillWaterOffsetY, z, w1.riverForce, flowDirectionZ)
					else
						-- Determine if the material type is allowed to change and change as needed.
						local holdMaterial = GetCell(c, x2 + drawX, endYs[drawX + halfWidth + 1] + drawDepth + fillWaterOffsetY, z).Value
						if changeTerrain == w1.changeToWaterMode and 0 ~= holdMaterial then
							SetWaterCell(c, x2 + drawX, endYs[drawX + halfWidth + 1] + drawDepth + fillWaterOffsetY, z, w1.riverForce, flowDirectionX)
						elseif changeAir == w1.changeToWaterMode and 0 == holdMaterial then
							SetWaterCell(c, x2 + drawX, endYs[drawX + halfWidth + 1] + drawDepth + fillWaterOffsetY, z, w1.riverForce, flowDirectionX)
						end
					end
				end
			end
		end
		
		-- This will draw air in the x direction.
		if (w1.fillAboveRiver and changeZ) then
			for fillHeight = 1, w1.tunnelHeight, 1 do
				HandleDrawYield()
				
				for drawX = -halfWidth, halfWidth, 1 do
					-- Only change to air if a water cell had been drawn above.
					if waterMaterialID == GetCell(c, x2 + drawX, highestRiverBlocks[drawX + halfWidth + 1], z).Value then
						if waterMaterialID ~= GetCell(c, x2 + drawX, fillHeight + highestRiverBlocks[drawX + halfWidth + 1], z).Value then
							SetCell(c, x2 + drawX, fillHeight + highestRiverBlocks[drawX + halfWidth + 1], z, w1.tunnelMaterial, 0, 0)
						end
					end
				end
			end
		end
	end
	
	-- The y continuation to use next time.
	if changeZ then 
		-- print('Change in Z ' .. endYForZ)
		return endYForZ
	else
		-- print('Change in X ' .. endYForX)
		return endYForX
	end
end

-- Function to connect to the mouse button 1 down event.  This is what will run when the user clicks.
-- It will determine what mode the plugin is in and what should be done with the click, how the river should be built.
-- mouse 	- Mouse data.
function onClicked (mouse)
	local cellPos
	
	if not on then 
		return
	end
	
	-- Ignore additional points when already drawing.
	if cancelValues.isDrawing then
		return
	end

		-- Make sure any old waypoints are gone.  This is done here so that they won't go away right away if immediate mode is changed.  The user has to click first.
		if drawImmediate == drawMode then	
			ClearWaypoints()
		end
	
	if on then
		c = game.Workspace.Terrain
		
		cellPos = WorldToCellPreferSolid(c, Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.Hit.z))
	
		-- Require clicking on solid ground.
		if 0 == GetCell(c, cellPos.X, cellPos.Y, cellPos.Z).Value then
			return
		end
		
		local x = cellPos.x
		local y = cellPos.y
		local z = cellPos.z
		
		continuation = false
		
		-- Create the start of a river/river section.
		if currentRiverOptions.startRiver then
			x1 = x
			z1 = z
			h = y
			currentRiverOptions.startRiver = false
			lastRiverY = y
			
			-- Waypoints should be stored.
			if drawWait == drawMode then
				local holdClone = currentRiverOptions:Clone(waypointValues.nextWaypointID)
				holdClone.highlight.selectionBox.Color = currentWaypointColor
				
				waypointValues.nextWaypointID = waypointValues.nextWaypointID + 1
				--holdClone:SetPosition(x1, h, z1)
				
				holdClone.x1 = x1
				holdClone.z1 = z1
				holdClone.h = h
				holdClone.highlight:UpdatePosition(Vector3.new(holdClone.x1, FindMaterialDown(holdClone.x1, Workspace.Terrain.Size.y, holdClone.z1), holdClone.z1))
								
				waypointValues.waypoints[#waypointValues.waypoints+1] = holdClone
				currentRiverOptions = holdClone
			else
				-- A immediate mode river was started, so get rid of any old waypoints.
				ClearWaypoints()
			end
		else
			x2 = x
			z2 = z
		
			-- Water is always going down.
			-- Use the click height as the draw height if it is less than the current height to draw at.
			-- Use the last y if it is lower still.
			if lastRiverY < h then
				h = lastRiverY
			end
			
			continuation = true
			
			-- Waypoints should be stored.
			if drawWait == drawMode then		
				local holdClone = currentRiverOptions:Clone(waypointValues.nextWaypointID)
				holdClone.highlight.selectionBox.Color = currentWaypointColor
				waypointValues.nextWaypointID = waypointValues.nextWaypointID + 1
				--holdClone:SetPosition(x2, h, z2)
	
				holdClone.x1 = x2
				holdClone.z1 = z2
				holdClone.h = h
				holdClone.highlight:UpdatePosition(Vector3.new(holdClone.x1, FindMaterialDown(holdClone.x1, Workspace.Terrain.Size.y, holdClone.z1), holdClone.z1))
				
				-- Added a new one, so change the color of the previous to be the non-last placed color.
				if 0 ~= #waypointValues.waypoints then
					waypointValues.waypoints[#waypointValues.waypoints].highlight.selectionBox.Color = waypointColor
				end				
				
				waypointValues.waypoints[#waypointValues.waypoints+1] = holdClone		
				currentRiverOptions = holdClone
			end
		
			
			if drawImmediate == drawMode then
				-- Only make the path if the start and end points are different. Otherwise it wouldn't be a path.  :)
				if true ~=(x1 == x2 and z1 == z2) then				
					-- Make the path in steps.
					startX = x1
					startZ = z1
					endX = x1
					endZ = z1
					
					changeX = x2 - x1
					changeZ = z2 - z1		
							
					
					-- For testing, uncomment when testing.
					--[[					
					print(' ')
					print('At Start')
					print('x1 ' .. x1)
					print('x2 ' .. x2)
					print('lastRiverY ' .. lastRiverY)
					print('z1 ' .. z1)
					print('z2 ' .. z2)
					--]]
					
					local startH = h
					
					LoadProgressBar()
					for step = 1, currentRiverOptions.riverSteps, 1 do					
						endX = (x1 + (step * (changeX / currentRiverOptions.riverSteps)))
						endZ = (z1 + (step * (changeZ / currentRiverOptions.riverSteps)))					
										
						-- Don't allow partial points.
						-- If this isn't done, then there can be sections that aren't attached.
						startX = math.floor(startX)
						endX = math.floor(endX)
						startZ = math.floor(startZ)
						endZ = math.floor(endZ)
						
						-- For testing, uncomment when testing.
						--print('In step ' .. startX .. ' ' .. endX .. ' ' .. startZ .. ' ' .. endZ)
						if not (continuation and ((startX == endX) and (startZ == endZ))) then
							-- Continue using the last y position.
							-- lastRiverY = makePath(startX, startZ, endX, endZ, h, continuation, lastRiverY, currentRiverOptions, nil)
							lastRiverY = makePath(startX, startZ, endX, endZ, startH, continuation, lastRiverY, currentRiverOptions, nil)
							startH = lastRiverY
								
							startX = endX
							startZ = endZ
						else
							--print('Ignored draw')
						end
						continuation = true
						
						cancelValues.setAmountFunc(step / currentRiverOptions.riverSteps)
						HandleDrawYield()
						
						if cancelValues.cancelAction then
							break
						end
					end
	
					UnloadProgressBar()
				end
			end
			
			-- Use the current point as the starting point of the next section.
			x1 = x2;
			z1 = z2;
		end
	end
end

mouseHighlighter.OnClicked = onClicked

-- Run when the popup is activated.
function On()
	plugin:Activate(true)
	toolbarbutton:SetActive(true)
	riverOptionsDragBar.Visible = true
	on = true
end

-- Run when the popup is deactivated.
function Off()
	toolbarbutton:SetActive(false)
	on = false
	
	-- Hide the popup gui.
	riverOptionsDragBar.Visible = false	
	mouseHighlighter:DisablePreview()
	
	EndCurrentRiver()
end

-- Made it to the end, it loaded.
loaded = true
