<?php

use Illuminate\Database\Eloquent\Collection;

class ActivitiesController extends \BaseController
{

    /**
     * Save a new activity
     *
     * @param int $id The id of the parent course
     * @return Response Redirect to the parent course's page
     */
    public function create($id)
    {
        /** Validation rules */

        $validator = Validator::make(
            array(
                'name' => Input::get('name'),
                'description' => Input::get('description')
            ),
            array(
                'name' => 'required|unique:activities,course_id,' . $id,
                'description' => 'required|min:10'
            )
        );


        /** If validation fails */
        if ($validator->fails()) {
            /** Prepare error message */
            $message = 'Error(s) creating a new Activity<ul>';

            foreach ($validator->messages()->all('<li>:message</li>') as $validationError) {
                $message .= $validationError;
            }

            $message .= '</ul>';

            /** Send error message and old data */
            Session::flash('status', 'danger');
            Session::flash('message', $message);
            return Redirect::back()->withInput();
        } else {
            /** Instantiate new activity */
            $activity = new Activity;
            $activity->name = Input::get('name');
            $activity->description = Input::get('description');
            $activity->course_id = $id;
            $activity->date = date('Y-m-d');

            /** If activity is saved, send success message */
            if ($activity->save()) {
                Session::flash('status', 'success');
                Session::flash('message', 'Activity created.');
                return Redirect::action('ActivitiesController@show', array($activity->id));
            }

            /** If saving fails, send error message and old data */
            else {
                Session::flash('status', 'warning');
                Session::flash('message', 'Error adding Activity. Please try again later.');
                return Redirect::back()->withInput();
            }
        }
    }

    public function newCreate($course_id = null)
    {
        $title = 'Create Activity';
        $activity_types = [];
        $instruments = Rubric::all();
        $courses = Course::where('user_id', Auth::user()->id)->get();
        $outcomes = Outcome::with('objectives')->get();
        //        var_dump($outcomes[0]->objectives);
        $objectives_by_outcome = Collection::make([]);
        $outcomes->each(function ($outcome) use (&$objectives_by_outcome) {
            //            var_dump($outcome->objectives);
            $objectives_by_outcome->put($outcome->id, $outcome->objectives);
            //            var_dump($objectives);
        });
        $criteria_by_objective = Collection::make([]);
        $objectives_by_outcome->each(function ($objectives) use (&$criteria_by_objective) {
            $objectives->each(function ($objective) use (&$criteria_by_objective) {
                $criteria_by_objective->put($objective->id, $objective->criteria);
            });
        });
        $transforming_actions = [];
        $course = Course::find($course_id);
        //        var_dump($criteria_by_objective);
        //        return $objectives->toJson();
        return View::make(
            'local.managers.admins.new-activity-create',
            compact(
                'title',
                'course',
                'activity_types',
                'instruments',
                'courses',
                'outcomes',
                'objectives_by_outcome',
                'criteria_by_objective',
                'transforming_actions'
            )
        );
    }

    /**
     *
     */
    public function show($id)
    {
        $activity = Activity::find($id);

        // If activity does not exist, display 404
        if (!$activity)
            App::abort('404');

        // Get activity's course
        $course = Course::where('id', '=', $activity->course_id)->firstOrFail();

        // If activity does not belong to the requesting user, display 403
        if ($course->user_id != Auth::id() and Auth::user()->role==4)
            App::abort('403', 'Access Forbidden');

        // Get active semesters
        $active_semesters = array();
        $active_semesters_collection = Semester::select('id')->where('is_visible', 1)->where('start', '<=', date('Y-m-d H:i:s'))->where('end', '>=', date('Y-m-d H:i:s'))->get();
        foreach ($active_semesters_collection as $active_semester) {
            $active_semesters[] = $active_semester->id;
        }
        // Added the function htmlspecialchars to activity name string because it was corrupting Jquery code while using quotes on page rendering. - Carlos R Caraballo 1/18/2019
        $title = $course->code . $course->number . '-' . $course->section . ': ' . htmlspecialchars($activity->name, ENT_QUOTES) . ' <span class="small attention">(' . $course->semester->code . ')</span>';
        $outcomes = Outcome::orderBy('name', 'asc')->get();
        $outcomes_achieved = json_decode($activity->outcomes_achieved, true);
        $outcomes_attempted = json_decode($activity->outcomes_attempted, true);

        return View::make('local.professors.activity', compact('activity', 'title', 'outcomes', 'outcomes_achieved', 'outcomes_attempted', 'course', 'student_count', 'active_semesters'));
    }


    public function assess($id)
    {
        $activity = Activity::find($id);

        // If activity does not exist, display 404
        if (!$activity)
            App::abort('404');

        // Get activity's course
        $course = Course::where('id', '=', $activity->course_id)->firstOrFail();

        // If activity does not belong to the requesting user, display 403
        if ($course->user_id != Auth::id())
            App::abort('403', 'Access Forbidden');

        $title = 'Assessment Sheet';
        $students = $course->students;

        // Get rubric contents
        $rubric = Rubric::find($activity->rubric_id);
        $rubric_contents = json_decode($rubric->contents);

        // Get results
        $assessments = DB::table('assessments')->join('students', 'assessments.student_id', '=', 'students.id')->where('activity_id', '=', $activity->id)->orderBy('assessments.id', 'asc')->get();

        // Decode the scores (blade workaround)
        $scores_array = array();
        foreach ($assessments as $index => $assessment) {
            $scores_array[$assessment->id] = json_decode($assessment->scores, true);
        }

        return View::make('local.professors.assessment', compact('activity', 'title', 'students', 'course', 'rubric_contents', 'assessments', 'scores_array', 'rubric'));
    }

    public function saveAssessment()
    {
        try {
            $exception = DB::transaction(function () {
                DB::transaction(function () {
                    // Student assessment data
                    $student_data = json_decode(Input::get('student_scores'));

                    // Outcome count
                    $outcomeCount = Outcome::all()->count();


                    // Activity
                    $activity = Activity::find(Input::get('activity_id'));

                    // Create or update student scores
                    if ($activity->outcomes_attempted == NULL) {
                        // For each student, save her/his assessment in the db
                        foreach ($student_data as $single_student_data) {
                            // Find student by id
                            $student = Student::find($single_student_data->student_id);

                            $comments = trim($single_student_data->comments);
                            if ($comments == '') {
                                $comments = NULL;
                            }

                            // Add the assessment to the pivot table
                            $student->assessed_activities()->attach($activity->id, array(
                                'scores' => json_encode($single_student_data->scores),
                                'percentage' => $single_student_data->percentage,
                                'comments' => $single_student_data->comments
                            ));
                        }
                    } else {
                        // For each student, save her/his assessment in the db
                        foreach ($student_data as $single_student_data) {
                            // Find student by id
                            $student = Student::find($single_student_data->student_id);

                            $comments = trim($single_student_data->comments);
                            if ($comments == '') {
                                $comments = NULL;
                            }

                            // Update the assessment in the pivot table
                            $student->assessed_activities()->updateExistingPivot($activity->id, array(
                                'scores' => json_encode($single_student_data->scores),
                                'percentage' => $single_student_data->percentage,
                                'comments' => $single_student_data->comments
                            ));
                        }
                    }


                    // Prepare arrays for criteria achievement for this activity
                    $criteria_achievement = json_decode(Input::get('criteria_achievement'));
                    $outcomes_attempted = array_fill(1, $outcomeCount, 0);
                    $outcomes_achieved = array_fill(1, $outcomeCount, 0);

                    // Fetch parent course's criteria achievement by outcome, if it exists
                    $course = $activity->course;
                    $course_outcomes_attempted = NULL;
                    $course_outcomes_achieved = NULL;

                    if ($course->outcomes_attempted == NULL) {
                        $course_outcomes_attempted = array_fill(1, $outcomeCount, 0);
                        $course_outcomes_achieved = array_fill(1, $outcomeCount, 0);
                    } else {
                        // the second argument is necessary to convert it into an array
                        $course_outcomes_attempted = json_decode($course->outcomes_attempted, true);
                        $course_outcomes_achieved = json_decode($course->outcomes_achieved, true);
                    }


                    foreach ($criteria_achievement as $criterion_id => $criterion_achieved) {
                        // Find corresponding learning outcome
                        $criterion = Criterion::withTrashed()->find($criterion_id);
                        $outcome = Outcome::find($criterion->outcome_id);

                        // If criterion is achieved (1), add 1 to all arrays
                        if ($criterion_achieved === 1) {
                            $outcomes_attempted[$outcome->id] += 1;
                            $outcomes_achieved[$outcome->id] += 1;
                            $course_outcomes_attempted[$outcome->id] += 1;
                            $course_outcomes_achieved[$outcome->id] += 1;
                        }
                        // Else if it's 0, only add to the attempted outcomes arrays
                        elseif ($criterion_achieved === 0) {
                            $outcomes_attempted[$outcome->id] += 1;
                            $course_outcomes_attempted[$outcome->id] += 1;
                        }
                    }

                    // If all values are 0, throw exception
                    if (count(array_unique($outcomes_attempted)) == 1 && $outcomes_attempted[1] == 0)
                        throw new Exception("Error Processing Request", 1);



                    // Set activity fields
                    $activity->criteria_achieved = Input::get('criteria_achievement');
                    $activity->criteria_achieved_percentage = Input::get('criteria_achieved_percentage');
                    $activity->outcomes_attempted = json_encode($outcomes_attempted);
                    $activity->outcomes_achieved = json_encode($outcomes_achieved);


                    // Publish results if not a draft. That is, update the activity's course.
                    if (Input::get('draft') == false) {
                        // Update course
                        $course->outcomes_achieved = json_encode($course_outcomes_achieved);
                        $course->outcomes_attempted = json_encode($course_outcomes_attempted);
                        $course->save();

                        $activity->draft = false;
                    } else {
                        // Set draft to true
                        $activity->draft = true;
                    }

                    // Save activity
                    $activity->save();


                    // Recalculate course outcomes
                    $activities = DB::table('activities')
                        ->where('course_id', $activity->course->id)
                        ->where('draft', 0)
                        ->get();


                    // Check if any assessed activities remain
                    $remainingAssessed = false;
                    foreach ($activities as $activity1) {
                        if ($activity1->outcomes_attempted != NULL) {
                            $remainingAssessed = true;
                            break;
                        }
                    }

                    //If there are still evaluated activities in the course, recalculate course outcomes
                    if (count($activities) && $remainingAssessed) {
                        $outcomeCount = Outcome::all()->count();

                        // Variables to hold recalculated outcomes for the course
                        $course_outcomes_attempted = array_fill(1, $outcomeCount, 0);
                        $course_outcomes_achieved = array_fill(1, $outcomeCount, 0);

                        // For each activity
                        foreach ($activities as $activity2) {
                            // If activity has been assessed
                            if ($activity2->outcomes_attempted != NULL) {
                                // Get the achieved criteria
                                $criteria_achievement = json_decode($activity2->criteria_achieved, true);
                                foreach ($criteria_achievement as $criterion_id => $criterion_achieved) {
                                    // Find corresponding learning outcome;
                                    $criterion = Criterion::withTrashed()->find($criterion_id);
                                    $outcome = Outcome::find($criterion->outcome_id);

                                    // If criterion is achieved (1), add 1 to both arrays
                                    if ($criterion_achieved === 1) {
                                        $course_outcomes_attempted[$outcome->id] += 1;
                                        $course_outcomes_achieved[$outcome->id] += 1;
                                    }
                                    // Else, only add to the attempted outcomes arrays
                                    elseif ($criterion_achieved === 0) {
                                        $course_outcomes_attempted[$outcome->id] += 1;
                                    }
                                }
                            }
                        }

                        // Update course
                        DB::table('courses')
                            ->where('id', $course->id)
                            ->update(array(
                                'outcomes_attempted' => json_encode($course_outcomes_attempted),
                                'outcomes_achieved' => json_encode($course_outcomes_achieved),
                                'updated_at' => date('Y-m-d H:i:s')
                            ));
                    }
                    // Otherwise, set them all to NULL
                    else {
                        DB::table('courses')
                            ->where('id', $course->id)
                            ->update(array(
                                'outcomes_attempted' => NULL,
                                'outcomes_achieved' => NULL,
                                'updated_at' => date('Y-m-d H:i:s')
                            ));
                    }
                });
            });

            if (is_null($exception)) {
                Session::flash('status', 'success');
                Session::flash('message', 'Assessment Saved. To add transforming actions click "Transforming Actions".');
                return action('ActivitiesController@show', array(Input::get('activity_id')));
            }
        } catch (Exception $e) {
            Log::info('e:' . $e);
            echo $e->getMessage();
            Session::flash('status', 'danger');
            Session::flash('message', 'Error saving assessment. Try again later.');

            return action('ActivitiesController@show', array(Input::get('activity_id')));
        }
    }

    public function deleteAssessment()
    {

        try {
            $exception = DB::transaction(function () {
                $activity = DB::table('activities')->where('id', Input::get('id'))->first();

                $course = DB::table('courses')->where('id', $activity->course_id)->first();


                // Reset results in activity
                DB::table('activities')
                    ->where('id', Input::get('id'))
                    ->update(array(
                        'draft' => 0,
                        'outcomes_attempted' => NULL,
                        'outcomes_achieved' => NULL,
                        'criteria_achieved' => NULL,
                        'transforming_actions' => NULL,
                        'assessment_comments' => NULL,
                        'criteria_achieved_percentage' => NULL,
                        'updated_at' => date('Y-m-d H:i:s')
                    ));

                // Delete students score
                DB::table('assessments')->where('activity_id', $activity->id)->delete();

                // Recalculate course outcomes
                $activities = DB::table('activities')
                    ->where('course_id', $course->id)
                    ->where('draft', 0)
                    ->get();


                // Check if any assessed activties remain
                $remainingAssessed = false;
                foreach ($activities as $activity) {
                    if ($activity->outcomes_attempted != NULL) {
                        $remainingAssessed = true;
                        break;
                    }
                }

                //If there are still evaluated activities in the course, recalculate course outcomes
                if (count($activities) && $remainingAssessed) {
                    $outcomeCount = Outcome::all()->count();

                    // Variables to hold recalculated outcomes for the course
                    $course_outcomes_attempted = array_fill(1, $outcomeCount, 0);
                    $course_outcomes_achieved = array_fill(1, $outcomeCount, 0);

                    // For each activity
                    foreach ($activities as $activity) {
                        // If activity has been assessed
                        if ($activity->outcomes_attempted != NULL) {
                            // Get the achieved criteria
                            $criteria_achievement = json_decode($activity->criteria_achieved, true);
                            foreach ($criteria_achievement as $criterion_id => $criterion_achieved) {
                                // Find corresponding learning outcome;
                                $criterion = Criterion::withTrashed()->find($criterion_id);
                                $outcome = Outcome::find($criterion->outcome_id);

                                // If criterion is achieved (1), add 1 to both arrays
                                if ($criterion_achieved === 1) {
                                    $course_outcomes_attempted[$outcome->id] += 1;
                                    $course_outcomes_achieved[$outcome->id] += 1;
                                }
                                // Else, only add to the attempted outcomes arrays
                                elseif ($criterion_achieved === 0) {
                                    $course_outcomes_attempted[$outcome->id] += 1;
                                }
                            }
                        }
                    }

                    // Update course
                    DB::table('courses')
                        ->where('id', $course->id)
                        ->update(array(
                            'outcomes_attempted' => json_encode($course_outcomes_attempted),
                            'outcomes_achieved' => json_encode($course_outcomes_achieved),
                            'updated_at' => date('Y-m-d H:i:s')
                        ));
                }
                // Otherwise, set them all to NULL
                else {
                    DB::table('courses')
                        ->where('id', $course->id)
                        ->update(array(
                            'outcomes_attempted' => NULL,
                            'outcomes_achieved' => NULL,
                            'updated_at' => date('Y-m-d H:i:s')
                        ));
                }
            });

            if (is_null($exception)) {
                Session::flash('status', 'success');
                Session::flash('message', 'Assessment deleted.');
                return Redirect::back();
            }
        } catch (Exception $e) {
            Session::flash('status', 'danger');
            Session::flash('message', 'Error saving  assessment. Try again later.');

            return Redirect::back();
        }
    }

    public function destroy($id)
    {
        $course = Activity::find($id)->course;

        if (Activity::destroy($id)) {
            // Recalculate course outcomes
            $activities = $course->activities;

            // Check if any assessed activties remain
            $remainingAssessed = false;
            foreach ($course->activities as $activity) {
                if ($activity->outcomes_attempted != NULL) {
                    $remainingAssessed = true;
                    break;
                }
            }

            //If there are still evaluated activities in the course, recalculate course outcomes
            if (!$course->activities->isEmpty() && $remainingAssessed) {
                $outcomeCount = Outcome::all()->count();

                // Variables to hold recalculated outcomes for the course
                $course_outcomes_attempted = array_fill(1, $outcomeCount, 0);
                $course_outcomes_achieved = array_fill(1, $outcomeCount, 0);

                // For each activity
                foreach ($activities as $activity) {
                    // If activity has been assessed
                    if ($activity->outcomes_attempted != NULL) {
                        // Get the achieved criteria
                        $criteria_achievement = json_decode($activity->criteria_achieved, true);
                        foreach ($criteria_achievement as $criterion_id => $criterion_achieved) {
                            // Find corresponding learning outcome;
                            $criterion = Criterion::withTrashed()->find($criterion_id);
                            $outcome = Outcome::find($criterion->outcome_id);

                            // If criterion is achieved (1), add 1 to both arrays
                            if ($criterion_achieved === 1) {
                                $course_outcomes_attempted[$outcome->id] += 1;
                                $course_outcomes_achieved[$outcome->id] += 1;
                            }
                            // Else, only add to the attempted outcomes arrays
                            elseif ($criterion_achieved === 0) {
                                $course_outcomes_attempted[$outcome->id] += 1;
                            }
                        }
                    }
                }

                // Update course
                $course->outcomes_achieved = json_encode($course_outcomes_achieved);
                $course->outcomes_attempted = json_encode($course_outcomes_attempted);
            } else {
                $course->outcomes_achieved = NULL;
                $course->outcomes_attempted = NULL;
            }

            if ($course->save()) {
                Session::flash('status', 'success');
                Session::flash('message', 'Activity deleted.');
            } else {
                Session::flash('status', 'danger');
                Session::flash('message', 'Error deleting activity. Try again later.');
                return Redirect::back();
            }

            return Redirect::action('CoursesController@show', array($course->id));
        } else {
            Session::flash('status', 'danger');
            Session::flash('message', 'Error deleting activity. Try again later.');
            return Redirect::back();
        }
    }

    public function update($id)
    {
        try {
            $activity = Activity::find($id);

            if (Input::has('update_activity_information')) {
                /** Validation rules */
                $validator = Validator::make(
                    array(
                        'name' => Input::get('name'),
                        'description' => Input::get('description'),
                        'date' => Input::get('date'),
                    ),
                    array(
                        'name' => 'required|unique:activities,course_id,' . $id,
                        'description' => 'required|min:10',
                        'date' => 'required|dateFormat:Y-m-d'
                    ),
                    array(
                        'date.dateFormat' => 'The date does not match the correct format: yyyy-mm-dd.'
                    )
                );

                /** If validation fails */
                if ($validator->fails()) {
                    /** Prepare error message */
                    $message = 'Error(s) updating the Activity<ul>';

                    foreach ($validator->messages()->all('<li>:message</li>') as $validationError) {
                        $message .= $validationError;
                    }

                    $message .= '</ul>';

                    /** Send error message and old data */
                    Session::flash('status', 'warning');
                    Session::flash('message', $message);
                    return Redirect::back()->withInput();
                }

                /** Update activity info */
                $activity->name = Input::get('name');
                $activity->description = Input::get('description');
                $activity->date = Input::get('date');
            } elseif (Input::has('update_transforming_actions')) {
                if (trim(Input::get('transforming_actions')) != "")
                    $activity->transforming_actions = Input::get('transforming_actions');
                else
                    $activity->transforming_actions = NULL;
            } elseif (Input::has('update_assessment_comments')) {
                if (trim(Input::get('assessment_comments')) != "")
                    $activity->assessment_comments = Input::get('assessment_comments');
                else
                    $activity->assessment_comments = NULL;
            } else {
                Session::flash('status', 'danger');
                Session::flash('message', 'Error updating Activity. Please try again later.');
                return Redirect::action('ActivitiesController@show', array($activity->id));
            }

            $activity->save();

            /** If activity is saved, send success message */
            Session::flash('status', 'success');
            Session::flash('message', 'Activity succesfully updated.');
            return Redirect::action('ActivitiesController@show', array($activity->id));
        } catch (Exception $e) {
            Session::flash('status', 'warning');
            Session::flash('message', 'Error updating Activity. Please try again later.');
            return Redirect::action('ActivitiesController@show', array($activity->id));
        }
    }

    //TODO the code in the next 2 functions is the same as the assess function except for the view returned. try to refactor this to avoid copying code.

    public function viewAssessment($id)
    {
        $activity = Activity::find($id);

        // If activity does not exist, display 404
        if (!$activity)
            App::abort('404');

        // Get activity's course
        $course = Course::where('id', '=', $activity->course_id)->firstOrFail();

        // If activity does not belong to the requesting user, display 403
        if ($course->user_id != Auth::id())
            App::abort('403', 'Access Forbidden');

        $title = 'Assessment Sheet';
        $students = $course->students;

        // Get rubric contents
        $rubric_contents = Rubric::select('contents')->where('id', '=', $activity->rubric_id)->get();
        $rubric_contents = json_decode($rubric_contents['0']->contents);

        $rubric = Rubric::find($activity->rubric_id);

        // Get results
        $assessments = DB::table('assessments')->where('activity_id', '=', $activity->id)->orderBy('id', 'asc')->get();

        // Decode the scores (blade workaround)
        $scores_array = array();
        foreach ($assessments as $index => $assessment) {
            $scores_array[$assessment->id] = json_decode($assessment->scores, true);
        }

        return View::make('local.professors.view_assessment', compact('activity', 'title', 'students', 'course', 'rubric_contents', 'assessments', 'scores_array', 'rubric'));
    }

    public function printAssessment($id)
    {
        $activity = Activity::find($id);

        // If activity does not exist, display 404
        if (!$activity)
            App::abort('404');

        // Get activity's course
        $course = Course::where('id', '=', $activity->course_id)->firstOrFail();

        // If activity does not belong to the requesting user, display 403
        if ($course->user_id != Auth::id())
            App::abort('403', 'Access Forbidden');

        $title = 'Assessment Sheet';
        $students = $course->students;

        // Get rubric contents
        $rubric_contents = Rubric::select('contents')->where('id', '=', $activity->rubric_id)->get();
        $rubric_contents = json_decode($rubric_contents['0']->contents);

        $rubric = Rubric::find($activity->rubric_id);

        // Get results
        $assessments = DB::table('assessments')->where('activity_id', '=', $activity->id)->orderBy('id', 'asc')->get();

        // Decode the scores (blade workaround)
        $scores_array = array();
        foreach ($assessments as $index => $assessment) {
            $scores_array[$assessment->id] = json_decode($assessment->scores, true);
        }

        return View::make('local.professors.print_assessment', compact('activity', 'title', 'students', 'course', 'rubric_contents', 'assessments', 'scores_array', 'rubric'));
    }
}