public class BranchBookingsActivity extends AppCompatActivity {

    //Automatic binding with Butterknife (external library)
    @Bind(R.id.booking_list)
    protected ListView bookingListView;
    @Bind(R.id.fab)
    protected FloatingActionButton floatingActionButton;

    //private members
    private CarRentalApiClient apiClient = CarRentalApiClientFactory.getApiClient();
    private BookingListAdapter bookingListAdapter;
    private BranchContract branchContract = PreferencesManager.getCurrentBranch(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user_bookings);

        ButterKnife.bind(this);
        ActivityUtils.buildDrawer(this);

        if (branchContract == null) {
            Toast toast = Toast.makeText(BranchBookingsActivity.this, R.string.invalid_branch, Toast.LENGTH_SHORT);
            toast.show();
            return;
        } else {
            setTitle(String.format(getString(R.string.branch_bookings_title), branchContract.name));
        }

        List<BookingContract> bookings = new ArrayList<>();

        bookingListAdapter = new BookingListAdapter(this, bookings);

        bookingListView.setAdapter(bookingListAdapter);

        bookingListView.setClickable(false);
        bookingListView.setItemsCanFocus(false);

        floatingActionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getBaseContext(), SearchAvailableVehicleActivity.class);
                startActivity(intent);
            }
        });

        new GetBookingsTask().start();
    }


//############################## TASKS #############################################################

    private class GetBookingsTask extends AsyncTask<Void, Void, List<BookingContract>> {

        ProgressDialog progressDialog;
        Exception apiException;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();

            progressDialog = new ProgressDialog(BranchBookingsActivity.this);
            progressDialog.setMessage(getString(R.string.loading_text));
            progressDialog.setIndeterminate(false);
            progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
            progressDialog.setCancelable(true);
            progressDialog.show();
        }

        @Override
        protected List<BookingContract> doInBackground(Void... params) {
            try {
                return apiClient.getBranchBookings();
            } catch (Exception e) {
                apiException = e;
                return null;
            }
        }

        @Override
        protected void onPostExecute(List<BookingContract> result) {
            Toast toast;
            progressDialog.dismiss();

            if (apiException != null) {
                ActivityUtils.HandleException(BranchBookingsActivity.this, apiException);
                return;
            }

            bookingListAdapter.setBookings(result);

            if (result.size() == 0) {
                toast = Toast.makeText(BranchBookingsActivity.this, R.string.no_booking_for_branch_at_the_moment, Toast.LENGTH_SHORT);
                toast.show();
            }

            progressDialog.dismiss();
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }

        public AsyncTask<Void, Void, List<BookingContract>> start(){
            return this.execute();
        }
    }

} public class ChooseUserForBookingActivity extends AppCompatActivity {

    //public message passing keys
    public final static String USER_SELECTED_SERIALIZED_KEY = "user_selected_serialized";

    //Automatic binding with Butterknife (external library)
    @Bind(R.id.user_list)
    protected ListView userListView;
    @Bind(R.id.search_user_field)
    protected EditText search_user_field;
    @Bind(R.id.create_new_user_account)
    protected Button createNewUserAccountButton;

    //private members
    private UserListAdapter userListAdapter;
    private SearchUserTask searchTask = null;
    private CarRentalApiClient apiClient = CarRentalApiClientFactory.getApiClient();
    private BookingSearchResultContract selectedBookingContract;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_choose_user_for_booking);

        ButterKnife.bind(this);
        ActivityUtils.buildDrawer(this);

        setTitle(getString(R.string.choose_user_title));

        Bundle bundle = getIntent().getExtras();

        selectedBookingContract = SerializationUtils.deserialize(bundle.getString(BookingSearchResultsActivity.BOOKING_SEARCH_RESULT_SERIALIZED_KEY), BookingSearchResultContract.class);

        List<UserContract> users = new ArrayList<>();

        userListAdapter = new UserListAdapter(this, users);

        userListView.setAdapter(userListAdapter);

        userListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                UserContract selectedUser = userListAdapter.getItem(position);
                Intent intent = new Intent(getBaseContext(), BookVehicleActivity.class);
                intent.putExtra(BookingSearchResultsActivity.BOOKING_SEARCH_RESULT_SERIALIZED_KEY, SerializationUtils.serialize(selectedBookingContract));
                intent.putExtra(USER_SELECTED_SERIALIZED_KEY, SerializationUtils.serialize(selectedUser));
                startActivity(intent);
            }
        });

        createNewUserAccountButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getBaseContext(), CreateUserActivity.class);
                intent.putExtra(BookingSearchResultsActivity.BOOKING_SEARCH_RESULT_SERIALIZED_KEY, SerializationUtils.serialize(selectedBookingContract));
                startActivity(intent);
            }
        });

        search_user_field.addTextChangedListener(new TextWatcher() {

            private Timer timer = new Timer();
            private final long DELAY_MS = 500;

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(final CharSequence s, int start, int before, int count) {
                //Will prevent the request to be thrown at each letter
                timer.cancel();
                timer = new Timer();
                timer.schedule(
                    new TimerTask() {
                        @Override
                        public void run() {
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    if (searchTask != null) {
                                        searchTask.cancel(true);
                                    }
                                    searchTask = new SearchUserTask(s.toString());
                                    searchTask.start();
                                }
                            });
                        }
                    },
                    DELAY_MS
                );
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });
    }

    //############################## TASKS #############################################################

    private class SearchUserTask extends AsyncTask<Void, Void, List<UserContract>> {

        SearchUserContract contract = new SearchUserContract();

        public SearchUserTask(String term) {
            contract.searchTerm = term;
        }

        ProgressDialog progressDialog;
        Exception apiException;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();

            progressDialog = new ProgressDialog(ChooseUserForBookingActivity.this);
            progressDialog.setMessage(getString(R.string.loading_text));
            progressDialog.setIndeterminate(false);
            progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
            progressDialog.setCancelable(true);
            progressDialog.show();
        }

        @Override
        protected List<UserContract> doInBackground(Void... params) {
            try {
                return apiClient.searchUser(contract);
            } catch (Exception e) {
                apiException = e;
                return null;
            }
        }

        @Override
        protected void onPostExecute(final List<UserContract> result) {
            progressDialog.dismiss();

            if (apiException != null) {
                ActivityUtils.HandleException(ChooseUserForBookingActivity.this, apiException);
                return;
            }

            userListAdapter.setUsers(result);

            if (result.size() == 0) {
                Toast toast = Toast.makeText(ChooseUserForBookingActivity.this, R.string.no_user_found_for_criteria, Toast.LENGTH_SHORT);
                toast.show();
            }

            progressDialog.dismiss();
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }

        public AsyncTask<Void, Void, List<UserContract>> start(){
            return this.execute();
        }
    }
} public class UserBookingsActivity extends AppCompatActivity {

    //Automatic binding with Butterknife (external library)
    @Bind(R.id.booking_list)
    protected ListView bookingListView;
    @Bind(R.id.fab)
    protected FloatingActionButton floatingActionButton;

    //private members
    private CarRentalApiClient apiClient = CarRentalApiClientFactory.getApiClient();
    private BookingListAdapter bookingListAdapter;
    private BranchContract branchContract = PreferencesManager.getCurrentBranch(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user_bookings);

        ButterKnife.bind(this);
        ActivityUtils.buildDrawer(this);

        if (branchContract == null) {
            Toast toast = Toast.makeText(UserBookingsActivity.this, R.string.invalid_branch, Toast.LENGTH_SHORT);
            toast.show();
            return;
        } else {
            setTitle(String.format(getString(R.string.user_bookings_title), branchContract.name));
        }

        List<BookingContract> bookings = new ArrayList<>();

        bookingListAdapter = new BookingListAdapter(this, bookings);

        bookingListView.setItemsCanFocus(false);
        bookingListView.setAdapter(bookingListAdapter);

        bookingListView.setClickable(false);

        floatingActionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getBaseContext(), SearchAvailableVehicleActivity.class);
                startActivity(intent);
            }
        });

        new GetBookingsTask().start();
    }


//############################## TASKS #############################################################

    /*
     * Fetches branches
     */
    private class GetBookingsTask extends AsyncTask<Void, Void, List<BookingContract>> {

        ProgressDialog progressDialog;
        Exception apiException;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();

            progressDialog = new ProgressDialog(UserBookingsActivity.this);
            progressDialog.setMessage(getString(R.string.loading_text));
            progressDialog.setIndeterminate(false);
            progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
            progressDialog.setCancelable(true);
            progressDialog.show();
        }

        @Override
        protected List<BookingContract> doInBackground(Void... params) {
            try {
                return apiClient.getUserBookingsForBranch();
            } catch (Exception e) {
                apiException = e;
                return null;
            }
        }

        @Override
        protected void onPostExecute(List<BookingContract> result) {
            Toast toast;
            progressDialog.dismiss();

            if (apiException != null) {
                ActivityUtils.HandleException(UserBookingsActivity.this, apiException);
                return;
            }

            bookingListAdapter.setBookings(result);

            if (result.size() == 0) {
                toast = Toast.makeText(UserBookingsActivity.this, R.string.no_booking_at_branch, Toast.LENGTH_SHORT);
                toast.show();
            }

            progressDialog.dismiss();
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }

        public AsyncTask<Void, Void, List<BookingContract>> start(){
            return this.execute();
        }
    }

} public class BranchListAdapter extends BaseAdapter implements Filterable {

    private List<BranchContract> originalData = null;
    private List<BranchContract> filteredData = null;
    private LayoutInflater layoutInflater;

    public BranchListAdapter(Context context, List<BranchContract> items) {
        layoutInflater = LayoutInflater.from(context);
        originalData = items;
        filteredData = items;
    }

    public void setBranches(List<BranchContract> branches) {
        this.originalData = branches;
        this.filteredData = branches;
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return filteredData.size();
    }

    @Override
    public BranchContract getItem(int position) {
        return filteredData.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View v = convertView;

        if (v == null) {
            v = layoutInflater.inflate(R.layout.branch_list_item, null);
        }

        BranchContract b = getItem(position);

        if (b != null) {
            Button button = (Button) v.findViewById(R.id.branch_list_button);

            if (button != null) {
                button.setText(b.name);
            }
        }

        return v;
    }

    @Override
    public Filter getFilter() {
        return new Filter() {

            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(CharSequence constraint,FilterResults results) {
                filteredData = (ArrayList<BranchContract>) results.values;
                notifyDataSetChanged();
            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                String filterPattern = constraint.toString().toLowerCase();

                FilterResults results = new FilterResults();
                ArrayList<BranchContract> resultList = new ArrayList<>(getCount());

                String currentBranchName;

                for (int i = 0; i < originalData.size(); i++) {
                    currentBranchName = originalData.get(i).name;
                    if (currentBranchName.toLowerCase().contains(filterPattern)) {
                        resultList.add(originalData.get(i));
                    }
                }

                results.values = resultList;
                results.count = resultList.size();

                return results;
            }
        };
    }
} public interface CarRentalApiClient {
    /**
     * Return the list of available branches
     *
     * @return list of branches
     * @throws ApiUnauthorizedException if the request wasn't authenticated or authorized
     * @throws ApiException if there was a general error during the request if there was a general error during the request
     * @throws ApiInvalidParameterException if there was an error with a parameter
     * @throws ApiUnavailableException if the API is unavailable
     */
    List<BranchContract> getBranches() throws ApiUnauthorizedException, ApiException, ApiInvalidParameterException, ApiUnavailableException;

    /**
     * Return the result of the available vehicles with the given criteria
     *
     * @param contract search criteria
     * @return list of available vehicle results
     * @throws ApiUnauthorizedException if the request wasn't authenticated or authorized
     * @throws ApiException if there was a general error during the request
     * @throws ApiInvalidParameterException if there was an error with a parameter
     * @throws ApiUnavailableException if the API is unavailable
     */
    List<BookingSearchResultContract> searchAvailableVehicles(SearchAvailableVehiclesRequestContract contract) throws ApiUnauthorizedException, ApiException, ApiInvalidParameterException, ApiUnavailableException;

    /**
     * Create an user account with the given parameters
     *
     * @param contract account parameters
     * @return created account
     * @throws ApiUnauthorizedException if the request wasn't authenticated or authorized
     * @throws ApiException if there was a general error during the request
     * @throws ApiInvalidParameterException if there was an error with a parameter
     * @throws ApiUnavailableException if the API is unavailable
     */
    UserContract createAccount(CreateAccountRequestContract contract) throws ApiUnauthorizedException, ApiException, ApiInvalidParameterException, ApiUnavailableException;

    /**
     * Returns the current user account details
     *
     * @return user account details
     * @throws ApiUnauthorizedException if the request wasn't authenticated or authorized
     * @throws ApiException if there was a general error during the request
     * @throws ApiInvalidParameterException if there was an error with a parameter
     * @throws ApiUnavailableException if the API is unavailable
     */
    UserContract getAccountDetails() throws ApiUnauthorizedException, ApiException, ApiInvalidParameterException, ApiUnavailableException;

    /**
     * Returns the bookings for the current user in the current branch
     *
     * @return list of bookings for the user and branch
     * @throws ApiUnauthorizedException if the request wasn't authenticated or authorized
     * @throws ApiException if there was a general error during the request
     * @throws ApiInvalidParameterException if there was an error with a parameter
     * @throws ApiUnavailableException if the API is unavailable
     */
    List<BookingContract> getUserBookingsForBranch() throws ApiUnauthorizedException, ApiException, ApiInvalidParameterException, ApiUnavailableException;

    /**
     * Request a vehicle booking with the given criteria
     *
     * @param contract booking criteria
     * @return created booking
     * @throws ApiUnauthorizedException if the request wasn't authenticated or authorized
     * @throws ApiException if there was a general error during the request
     * @throws ApiInvalidParameterException if there was an error with a parameter
     * @throws ApiUnavailableException if the API is unavailable
     */
    BookingContract bookVehicle(CreateBookingContract contract) throws ApiUnauthorizedException, ApiException, ApiInvalidParameterException, ApiUnavailableException;

    /**
     * Returns all the bookings for the current branch
     *
     * @return the branch bookings list
     * @throws ApiUnauthorizedException if the request wasn't authenticated or authorized
     * @throws ApiException if there was a general error during the request
     * @throws ApiInvalidParameterException if there was an error with a parameter
     * @throws ApiUnavailableException if the API is unavailable
     */
    List<BookingContract> getBranchBookings() throws ApiUnauthorizedException, ApiException, ApiInvalidParameterException, ApiUnavailableException;

    /**
     * Sends a shutdown request to the server
     *
     * @return nothing
     * @throws ApiUnauthorizedException if the request wasn't authenticated or authorized
     * @throws ApiException if there was a general error during the request
     * @throws ApiInvalidParameterException if there was an error with a parameter
     * @throws ApiUnavailableException if the API is unavailable
     */
    Void shutdownSystem() throws ApiUnauthorizedException, ApiException, ApiInvalidParameterException, ApiUnavailableException;

    /**
     * Create an user account with the given parameters (requires staff level)
     *
     * @param contract the account parameters
     * @return the created account
     * @throws ApiUnauthorizedException if the request wasn't authenticated or authorized
     * @throws ApiException if there was a general error during the request
     * @throws ApiInvalidParameterException if there was an error with a parameter
     * @throws ApiUnavailableException if the API is unavailable
     */
    UserContract createUser(CreateAccountRequestContract contract)  throws ApiUnauthorizedException, ApiException, ApiInvalidParameterException, ApiUnavailableException;

    /**
     * Create or update a vehicle with the given parameters in the current branch
     *
     * @param contract vehicle parameters
     * @return created or updated vehicle
     * @throws ApiUnauthorizedException if the request wasn't authenticated or authorized
     * @throws ApiException if there was a general error during the request
     * @throws ApiInvalidParameterException if there was an error with a parameter
     * @throws ApiUnavailableException if the API is unavailable
     */
    VehicleContract createOrUpdateVehicle(CreateUpdateVehicleContract contract)  throws ApiUnauthorizedException, ApiException, ApiInvalidParameterException, ApiUnavailableException;

    /**
     * Search vehicles with the given criteria in the current branch
     *
     * @param contract search criteria
     * @return list of vehicles matching the criteria
     * @throws ApiUnauthorizedException if the request wasn't authenticated or authorized
     * @throws ApiException if there was a general error during the request
     * @throws ApiInvalidParameterException if there was an error with a parameter
     * @throws ApiUnavailableException if the API is unavailable
     */
    List<VehicleContract> searchVehicles(SearchVehicleContract contract)  throws ApiUnauthorizedException, ApiException, ApiInvalidParameterException, ApiUnavailableException;

    /**
     * Search users with the given criteria (full text search on email and fullname)
     *
     * @param contract search criteria
     * @return list of users matching the criteria
     * @throws ApiUnauthorizedException if the request wasn't authenticated or authorized
     * @throws ApiException if there was a general error during the request
     * @throws ApiInvalidParameterException if there was an error with a parameter
     * @throws ApiUnavailableException if the API is unavailable
     */
    List<UserContract> searchUser(SearchUserContract contract)  throws ApiUnauthorizedException, ApiException, ApiInvalidParameterException, ApiUnavailableException;

    /**
     * Update a booking status with the given parameters
     *
     * @param contract update parameters
     * @return updated booking
     * @throws ApiUnauthorizedException if the request wasn't authenticated or authorized
     * @throws ApiException if there was a general error during the request
     * @throws ApiInvalidParameterException if there was an error with a parameter
     * @throws ApiUnavailableException if the API is unavailable
     */
    BookingContract changeBookingStatus(ChangeBookingStatusContract contract)  throws ApiUnauthorizedException, ApiException, ApiInvalidParameterException, ApiUnavailableException;

    /**
     * Returns the vehicle moves for the current branch matching the filter criteria
     *
     * @param contract filter criteria
     * @return list of bookings requiring moves
     * @throws ApiUnauthorizedException if the request wasn't authenticated or authorized
     * @throws ApiException if there was a general error during the request
     * @throws ApiInvalidParameterException if there was an error with a parameter
     * @throws ApiUnavailableException if the API is unavailable
     */
    List<BookingContract> getVehiclesMove(GetBranchVehicleMovesContract contract)  throws ApiUnauthorizedException, ApiException, ApiInvalidParameterException, ApiUnavailableException;
} public class CarRentalApiClientFactory {
    /**
     * Creates a new API client with the default IP and port values
     *
     * @return api client
     */
    public static CarRentalApiClient getApiClient() {
        return new CarRentalApiClientSocketImpl(
                PreferencesManager.getHeadOfficeIpAddress(CarRentalApplication.getAppContext()),
                PreferencesManager.getHeadOfficePort(CarRentalApplication.getAppContext()));
    }

    /**
     * Creates a new API client with the given IP and port values
     * @param ipAddress given ip address
     * @param port given port
     * @return api client
     */
    public static CarRentalApiClient getApiClient(String ipAddress, int port) {
        return new CarRentalApiClientSocketImpl(ipAddress, port);
    }
} public class BranchContract {
    public int id;
    public String name;
    public double latitude;
    public double longitude;
} public class UserContract {
    public int id;
    public boolean isStaff;
    public String fullName;
    public String emailAddress;
    public String phoneNumber;
} public class PreferencesManager {

    //SharedPreferences storage keys
    private static final String PREF_USER_LABEL = "logged_user";
    private static final String PREF_PASSWORD_LABEL = "password_user";
    private static final String PREF_BRANCH_LABEL = "current_branch";
    private static final String PREF_IP_ADDRESS_LABEL = "server_ip";
    private static final String PREF_PORT_LABEL = "server_port";

    //Default values for the connection system
    private static final String DEFAULT_IP_ADDRESS = "161.73.147.225";
    private static final int DEFAULT_PORT = 5106;

    //In-Memory cache, avoids a reload from the shared preferences every time
    private static UserContract currentUser;
    private static BranchContract currentBranch;
    private static String userPassword;
    private static String ipAddress;
    private static Integer port;

    /**
     * Returns the SharedPreferences for the given context
     *
     * @param ctx current context
     * @return SharedPreferences instance
     */
    private static SharedPreferences getSharedPreferences(Context ctx) {
        return PreferenceManager.getDefaultSharedPreferences(ctx);
    }

    /**
     * Updates the current branch
     *
     * @param ctx current context
     * @param contract newly selected branch
     */
    public static void setCurrentBranch(Context ctx, BranchContract contract) {
        SharedPreferences.Editor editor = getSharedPreferences(ctx).edit();
        editor.putString(PREF_BRANCH_LABEL, SerializationUtils.serialize(contract));
        editor.apply();
        currentBranch = contract;
    }

    /**
     * Returns the current branch, loads it from the SharedPreferences if not yet in memory
     *
     * @param ctx current context
     * @return the current branch
     */
    public static BranchContract getCurrentBranch(Context ctx) {
        if (currentBranch == null) {
            currentBranch = SerializationUtils.deserialize(getSharedPreferences(ctx).getString(PREF_BRANCH_LABEL, null), BranchContract.class);
        }

        return currentBranch;
    }

    /**
     * Returns the current authentication state
     *
     * @param ctx current context
     * @return true if logged in, false otherwise
     */
    public static boolean isLoggedIn(Context ctx) {
        UserContract loggedUser = getLoggedUser(ctx);

        return !(loggedUser == null);
    }

    /**
     * Returns the current staff authorization state
     *
     * @param ctx current context
     * @return true if logged in AND staff member, false otherwise
     */
    public static boolean isStaffUser(Context ctx) {
        UserContract loggedUser = getLoggedUser(ctx);

        return loggedUser != null && loggedUser.isStaff;
    }

    /**
     * Returns the current logged user, loads it from the SharedPreferences if not yet in memory
     *
     * @param ctx current context
     * @return the current user if logged in, null otherwise
     */
    public static UserContract getLoggedUser(Context ctx) {
        if (currentUser == null) {
            currentUser = SerializationUtils.deserialize(getSharedPreferences(ctx).getString(PREF_USER_LABEL, null), UserContract.class);
        }

        return currentUser;
    }

    /**
     * Updates the current user
     *
     * @param ctx current context
     * @param user current user
     */
    public static void setLoggedUser(Context ctx, UserContract user) {
        SharedPreferences.Editor editor = getSharedPreferences(ctx).edit();
        editor.putString(PREF_USER_LABEL, SerializationUtils.serialize(user));
        currentUser = user;
        editor.apply();
    }

    /**
     * Remove the current user settings
     *
     * @param ctx current context
     */
    public static void clearSession(Context ctx) {
        SharedPreferences.Editor editor = getSharedPreferences(ctx).edit();
        editor.remove(PREF_USER_LABEL);
        editor.remove(PREF_PASSWORD_LABEL);
        editor.remove(PREF_BRANCH_LABEL);
        currentUser = null;
        userPassword = null;
        currentBranch = null;
        editor.apply();
    }

    /**
     * Change the current user password
     *
     * @param ctx current context
     * @param password new password
     */
    public static void setUserPassword(Context ctx, String password) {
        SharedPreferences.Editor editor = getSharedPreferences(ctx).edit();
        editor.putString(PREF_PASSWORD_LABEL, password);
        editor.apply();
        userPassword = password;
    }

    /**
     * Returns the current user password, loads it if not done yet
     *
     * @param ctx current context
     * @return current password
     */
    public static String getUserPassword(Context ctx) {
        if (userPassword == null) {
            userPassword = getSharedPreferences(ctx).getString(PREF_PASSWORD_LABEL, null);
        }

        return userPassword;
    }

    /**
     * Update head office IP address
     *
     * @param ctx current context
     * @param newIpAddress new IP address
     */
    public static void setHeadOfficeIpAddress(Context ctx, String newIpAddress) {
        SharedPreferences.Editor editor = getSharedPreferences(ctx).edit();
        editor.putString(PREF_IP_ADDRESS_LABEL, newIpAddress);
        ipAddress = newIpAddress;
        editor.apply();
    }

    /**
     * Returns the current head office IP address, loads it if not done yet
     * @param ctx current context
     * @return the current head office IP address
     */
    public static String getHeadOfficeIpAddress(Context ctx) {
        if (ipAddress == null) {
            ipAddress = getSharedPreferences(ctx).getString(PREF_IP_ADDRESS_LABEL, null);

            if (ipAddress == null) {
                setHeadOfficeIpAddress(ctx, DEFAULT_IP_ADDRESS);
            }
        }

        return ipAddress;
    }

    /**
     * Update head office port
     *
     * @param ctx current context
     * @param newPort new port
     */
    public static void setHeadOfficePort(Context ctx, int newPort) {
        SharedPreferences.Editor editor = getSharedPreferences(ctx).edit();
        editor.putInt(PREF_PORT_LABEL, newPort);
        port = newPort;
        editor.apply();
    }

    /**
     * Returns the current head office port, loads it if not done yet
     * @param ctx current context
     * @return the current head office port
     */
    public static int getHeadOfficePort(Context ctx) {
        if (port == null) {
            port = getSharedPreferences(ctx).getInt(PREF_PORT_LABEL, -1);

            // 0 Should not be used as a port anyway.
            if (port <= 0) {
                setHeadOfficePort(ctx, DEFAULT_PORT);
            }
        }

        return port;
    }
} public class ActivityUtils {

    /**
     * This method will display an error message depending on which error has been thrown by the API
     *
     * @param context current context
     * @param apiException expression thrown by the API client
     */
    public static void HandleException(Context context, Exception apiException) {
        Toast toast;
        if (apiException instanceof ApiUnauthorizedException) {
            toast = Toast.makeText(context, R.string.error_invalid_account_credentials, Toast.LENGTH_SHORT);
            PreferencesManager.clearSession(context);
        } else if (apiException instanceof ApiInvalidParameterException) {
            toast = Toast.makeText(context, String.format(context.getString(R.string.ends_with_a_dot), apiException.getMessage()), Toast.LENGTH_SHORT);
        } else if (apiException instanceof ApiUnavailableException) {
            toast = Toast.makeText(context, R.string.error_server_not_available, Toast.LENGTH_SHORT);
        } else {
            toast = Toast.makeText(context, R.string.error_server_not_reachable, Toast.LENGTH_SHORT);
        }
        toast.show();
    }

    /**
     * This method will build the left drawer on the android application in every activity which calls it
     *
     * The drawer is composed of many different items, each of them executing a specific action when clicked
     *
     * @param activity the current activity
     */
    public static void buildDrawer(final AppCompatActivity activity) {
        BranchContract branchContract = PreferencesManager.getCurrentBranch(activity);
        String branchName = branchContract == null ? activity.getString(R.string.no_branch_selected) : branchContract.name;

        /* Login */
        PrimaryDrawerItem loginItem = new PrimaryDrawerItem()
            .withIcon(R.drawable.ic_perm_identity_black_24dp)
            .withName(R.string.login_register_menu_item)
            .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
                @Override
                public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
                    PreferencesManager.clearSession(activity);
                    Intent intent = new Intent(CarRentalApplication.getAppContext(), LoginActivity.class);
                    //intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                    activity.startActivity(intent);
                    return true;
                }
            }
        );

        /* Change connection settings */
        PrimaryDrawerItem changeIpSettings = new PrimaryDrawerItem()
                .withIcon(R.drawable.ic_settings_black_24dp)
                .withName(R.string.change_server_settings_menu_item)
                .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
                                                   @Override
                                                   public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
                    Intent intent = new Intent(CarRentalApplication.getAppContext(), ChangeConnectionSettingsActivity.class);
                    intent.putExtra(LoginActivity.COMES_FROM_LOGIN, false);
                    activity.startActivity(intent);
                    return true;
               }
            }
        );

        /* Logout */
        PrimaryDrawerItem logoutItem = new PrimaryDrawerItem()
            .withIcon(R.drawable.ic_perm_identity_black_24dp)
            .withName(R.string.logout_menu_item)
            .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
                @Override
                public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
                    PreferencesManager.clearSession(activity);
                    Intent intent = new Intent(CarRentalApplication.getAppContext(), LoginActivity.class);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                    activity.startActivity(intent);
                    return true;
                }
            }
        );

        /* Change Branch */
        PrimaryDrawerItem changeBranchItem = new PrimaryDrawerItem()
            .withIcon(R.drawable.ic_room_black_24dp)
            .withName(String.format(activity.getString(R.string.change_branch_menu_item), branchName))
            .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
                @Override
                public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
                    Intent intent = new Intent(CarRentalApplication.getAppContext(), ChooseBranchActivity.class);
                    activity.startActivity(intent);
                    return true;
                }
            }
        );

        /* Search Vehicle */
        PrimaryDrawerItem searchBookingItem = new PrimaryDrawerItem()
            .withIcon(R.drawable.ic_search_black_24dp)
            .withName(R.string.search_available_vehicles_menu_item)
            .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
                @Override
                public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
                    Intent intent = new Intent(CarRentalApplication.getAppContext(), SearchAvailableVehicleActivity.class);
                    activity.startActivity(intent);
                    return true;
                }
            }
        );

        /* USER: Show user bookings */
        PrimaryDrawerItem userBookings = new PrimaryDrawerItem()
            .withIcon(R.drawable.ic_view_list_black_24dp)
            .withName(R.string.user_bookings_menu_item)
            .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
                @Override
                public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
                    Intent intent = new Intent(CarRentalApplication.getAppContext(), UserBookingsActivity.class);
                    activity.startActivity(intent);
                    return true;
                }
            }
        );

        /* ADMIN: Search vehicle */
        PrimaryDrawerItem searchVehicleItem = new PrimaryDrawerItem()
            .withIcon(R.drawable.ic_search_black_24dp)
            .withName(R.string.search_vehcile_menu_item)
            .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
                                               @Override
                                               public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
                   Intent intent = new Intent(CarRentalApplication.getAppContext(), SearchVehicleActivity.class);
                   activity.startActivity(intent);
                   return true;
                }
            }
        );

        /* ADMIN: DO Client booking vehicle */
        PrimaryDrawerItem bookForClientItem = new PrimaryDrawerItem()
            .withIcon(R.drawable.ic_directions_car_black_24dp)
            .withName(R.string.make_client_booking_menu_item)
            .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
                                               @Override
                                               public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
                   Intent intent = new Intent(CarRentalApplication.getAppContext(), SearchAvailableVehicleActivity.class);
                   activity.startActivity(intent);
                   return true;
                    }
            }
        );

        /* ADMIN: AddVehicle */
        PrimaryDrawerItem addVehicleItem = new PrimaryDrawerItem()
            .withIcon(R.drawable.ic_add_black_24dp)
            .withName(R.string.add_vehicle_menu_item)
            .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
                                                   @Override
                                                   public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
                   Intent intent = new Intent(CarRentalApplication.getAppContext(), CreateVehicleActivity.class);
                   activity.startActivity(intent);
                   return true;
                                                   }
            }
        );

        /* ADMIN: Show branch bookings */
        PrimaryDrawerItem showBranchBookingsItem = new PrimaryDrawerItem()
            .withIcon(R.drawable.ic_view_list_black_24dp)
            .withName(R.string.view_branch_bookings_menu_item)
            .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
                                               @Override
                                               public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
                   Intent intent = new Intent(CarRentalApplication.getAppContext(), BranchBookingsActivity.class);
                   activity.startActivity(intent);
                   return true;
                                               }
            }
        );

        /* ADMIN: Show Moves */
        PrimaryDrawerItem showMovesItem = new PrimaryDrawerItem()
            .withIcon(R.drawable.ic_swap_horiz_black_24dp)
            .withName(R.string.view_branch_moves_menu_item)
            .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
                                                   @Override
                                                   public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
                   Intent intent = new Intent(CarRentalApplication.getAppContext(), ShowBranchMovesActivity.class);
                   activity.startActivity(intent);
                   return true;
                                                   }
            }
        );

        /* ADMIN: sys admin */
        PrimaryDrawerItem sysAdminItem = new PrimaryDrawerItem()
            .withIcon(R.drawable.ic_power_settings_new_black_24dp)
            .withName(R.string.server_control_menu_item)
            .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
                                                   @Override
                                                   public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
                    Intent intent = new Intent(CarRentalApplication.getAppContext(), SystemAdministrationActivity.class);
                    activity.startActivity(intent);
                    return true;
                                                   }
            }
        );

        //Instantiate the builder
        DrawerBuilder drawerBuilder = new DrawerBuilder()
                .withActivity(activity)
                .withDisplayBelowStatusBar(false)
                .withTranslucentStatusBar(false)
                .withSelectedItem(-1);

        //Instantiate the header builder
        AccountHeaderBuilder headerBuilder = new AccountHeaderBuilder()
                .withActivity(activity)
                .withHeaderBackground(R.drawable.drawer_header)
                .withSelectionListEnabledForSingleProfile(false);

        //Finalize configuration, depending on user leverl
        if (PreferencesManager.isLoggedIn(activity)) {
            UserContract user = PreferencesManager.getLoggedUser(activity);

            if (PreferencesManager.isStaffUser(activity)) {
                //Staff drawer
                AccountHeader headerResult = headerBuilder
                    .addProfiles(new ProfileDrawerItem()
                        .withName(String.format(activity.getString(R.string.staff_user_label), user.fullName))
                        .withEmail(user.emailAddress)
                        .withIcon(R.drawable.icon))
                    .build();

                drawerBuilder
                    .withAccountHeader(headerResult)
                    .addDrawerItems(logoutItem, changeBranchItem, searchVehicleItem, addVehicleItem, bookForClientItem, showBranchBookingsItem, showMovesItem, sysAdminItem, changeIpSettings);
            } else {
                //User drawer
                AccountHeader headerResult = headerBuilder
                    .addProfiles(new ProfileDrawerItem()
                        .withName(user.fullName)
                        .withEmail(user.emailAddress)
                        .withIcon(R.drawable.icon))
                    .build();

                drawerBuilder
                    .withAccountHeader(headerResult)
                    .addDrawerItems(logoutItem, changeBranchItem, searchBookingItem, userBookings, changeIpSettings);
            }
        } else {
            //User drawer
            AccountHeader headerResult = headerBuilder
                .addProfiles(new ProfileDrawerItem()
                    .withName(activity.getString(R.string.not_logged_in))
                    .withIcon(R.drawable.icon))
                .build();

            drawerBuilder
                .withAccountHeader(headerResult)
                .addDrawerItems(loginItem, changeBranchItem, searchBookingItem, changeIpSettings);
        }

        //Build the drawer
        Drawer drawer = drawerBuilder.build();
    }
} public class LocationUtils {

    //Constants used in the calculation
    private final static double EARTH_RADIUS_IN_KM = 6371;

    /**
     * Returns the nearest branch to the user position
     * This is not a particularly powerful algorithm but the number of branch is expected to be small at this time
     *
     * @param currentLocation user location
     * @param branchContractList list of branches
     * @return the nearest branch
     */
    public static BranchContract getNearestBranch(Location currentLocation, List<BranchContract> branchContractList) {
        if (branchContractList.size() == 0) {
            return null;
        }

        if (currentLocation == null) {
            return null;
        }

        BranchContract nearestBranch = branchContractList.get(0);
        double nearestBranchDistance = getDistance(currentLocation.getLatitude(), currentLocation.getLongitude(), nearestBranch.latitude, nearestBranch.longitude);

        for (BranchContract branchContract : branchContractList) {
            double currentBranchDistance = getDistance(currentLocation.getLatitude(), currentLocation.getLongitude(), branchContract.latitude, branchContract.longitude);
            if (currentBranchDistance < nearestBranchDistance) {
                nearestBranch = branchContract;
                nearestBranchDistance = currentBranchDistance;
            }
        }

        return nearestBranch;
    }

    /**
     * Calculates the spacial distance between two points using Haversine formula
     *
     * @param lat1 latitude of the first point
     * @param lng1 longitude of the first point
     * @param lat2 latitude of the second point
     * @param lng2 longitude of the second point
     * @return the distance between the two points
     */
    private static double getDistance(double lat1, double lng1, double lat2, double lng2) {
        Double latDistance = degreesToRadians(lat2-lat1);
        Double lgnDistance = degreesToRadians(lng2-lng1);

        Double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2) +
                Math.cos(degreesToRadians(lat1)) * Math.cos(degreesToRadians(lat2)) *
                        Math.sin(lgnDistance / 2) * Math.sin(lgnDistance / 2);

        Double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

        return EARTH_RADIUS_IN_KM * c;
    }

    /**
     * Converts degrees value to radians
     *
     * @param degrees degrees value
     * @return radians value
     */
    private static double degreesToRadians(double degrees) {
        return (degrees * Math.PI / 180.0);
    }
} public class StaticDataUtils {

    //Data structures to provide an easy access to the static data
    private static ArrayList<String> vehicleTypes;
    private static ArrayList<String> vehicleStatuses;
    private static Map<String, BranchContract> branchContractMap;

    /**
     * Registers all the available vehicle types in memory
     */
    private static void initializeVehicleTypes() {
        vehicleTypes = new ArrayList<>();
        vehicleTypes.add(0, CarRentalApplication.getAppContext().getString(R.string.small_car_type));
        vehicleTypes.add(1, CarRentalApplication.getAppContext().getString(R.string.family_car_type));
        vehicleTypes.add(2, CarRentalApplication.getAppContext().getString(R.string.small_van_type));
        vehicleTypes.add(3, CarRentalApplication.getAppContext().getString(R.string.large_van_type));
    }

    /**
     * Registers all the available vehicle status in memory
     */
    private static void initializeVehicleStatuses() {
        vehicleStatuses = new ArrayList<>();
        vehicleStatuses.add(0, CarRentalApplication.getAppContext().getString(R.string.available_status));
        vehicleStatuses.add(1, CarRentalApplication.getAppContext().getString(R.string.in_transit_status));
        vehicleStatuses.add(2, CarRentalApplication.getAppContext().getString(R.string.in_client_booking_status));
        vehicleStatuses.add(3, CarRentalApplication.getAppContext().getString(R.string.maintenance_status));
    }

    /**
     * Registers the branches in memory
     * We are using a hashmap, because the branches will be mapped through their name
     *
     * @param branches the branch list
     */
    public static void setBranches(List<BranchContract> branches) {
        branchContractMap = new HashMap<>();
        for (BranchContract branch : branches) {
            branchContractMap.put(branch.name, branch);
        }
    }

    /**
     * Returns the list of vehicle types, and initialize it if it has not been done yet
     *
     * @return the vehicle types list
     */
    public static ArrayList<String> getVehicleTypesList() {
        if (vehicleTypes == null) {
            initializeVehicleTypes();
        }

        return vehicleTypes;
    }

    /**
     * Returns the list of vehicle types as an array, and initialize it if it has not been done yet
     *
     * @return the vehicle types array
     */
    public static String[] getVehicleTypesArray() {
        return getVehicleTypesList().toArray(new String[getVehicleTypesList().size()]);
    }

    /**
     * Returns the list of vehicle statuses, and initialize it if it has not been done yet
     *
     * @return the vehicle statuses list
     */
    public static ArrayList<String> getVehicleStatusesList() {
        if (vehicleStatuses == null) {
            initializeVehicleStatuses();
        }

        return vehicleStatuses;
    }

    /**
     * Returns the list of vehicle statuses as an array, and initialize it if it has not been done yet
     *
     * @return the vehicle statuses array
     */
    public static String[] getVehicleStatusesArray() {
        return getVehicleStatusesList().toArray(new String[getVehicleStatusesList().size()]);
    }

    /**
     * Returns the array of the branch names
     *
     * @return the branch names arrays
     */
    public static String[] getBranchNamesArray() {
        return branchContractMap.keySet().toArray(new String[branchContractMap.size()]);
    }

    /**
     * Return the branch matching the given name
     *
     * @param name given branch name
     * @return the branch if it exists, null otherwise
     */
    public static BranchContract getBranchFromName(String name) {
        return branchContractMap.get(name);
    }
}
import android.app.ProgressDialog;
import android.content.Intent;
import android.location.Location;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import com.vehiclerental.R;
import com.vehiclerental.activities.Staff.BranchBookingsActivity;
import com.vehiclerental.activities.Staff.ChooseUserForBookingActivity;
import com.vehiclerental.activities.User.UserBookingsActivity;
import com.vehiclerental.adapters.BranchListAdapter;
import com.vehiclerental.apiClient.CarRentalApiClient;
import com.vehiclerental.apiClient.CarRentalApiClientFactory;
import com.vehiclerental.contracts.BranchContract;
import com.vehiclerental.contracts.UserContract;
import com.vehiclerental.preferences.PreferencesManager;
import com.vehiclerental.utils.ActivityUtils;
import com.vehiclerental.utils.LocationUtils;
import com.vehiclerental.utils.StaticDataUtils;
import java.util.ArrayList;
import java.util.List;
import butterknife.Bind;
import butterknife.ButterKnife;
import io.nlopez.smartlocation.OnLocationUpdatedListener;
import io.nlopez.smartlocation.SmartLocation;
import io.nlopez.smartlocation.location.providers.LocationGooglePlayServicesProvider;
/**
 * CarRental
 *
 * This activity asks for the user to choose a branch in order to use the application
 * A list of branch is fetched from the server and displayed with a text-based filter
 *
 * Also, the user localisation is used in order to propose the nearest branch to the localisation
 */

package com.vehiclerental.activities.Guest;





public class ChooseBranchActivity extends AppCompatActivity {

    //Automatic binding with Butterknife (external library)
    @Bind(R.id.branch_list)
    protected ListView branchListView;
    @Bind(R.id.nearest_branch_button)
    protected Button nearestBranchButton;
    @Bind(R.id.search_branch_field)
    protected EditText search_branch_field;
    @Bind(R.id.nearest_branch_label)
    protected TextView nearestBranchLabel;

    //private members
    private BranchListAdapter branchListAdapter;
    private BranchContract nearestBranch;
    private CarRentalApiClient apiClient = CarRentalApiClientFactory.getApiClient();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_choose_branch);

        ButterKnife.bind(this);

        branchListView.setDivider(null);
        branchListView.setDividerHeight(0);

        //Geolocation
        LocationGooglePlayServicesProvider provider = new LocationGooglePlayServicesProvider();
        provider.setCheckLocationSettings(true);

        List<BranchContract> branches = new ArrayList<>();

        branchListAdapter = new BranchListAdapter(this, branches);

        branchListView.setAdapter(branchListAdapter);

        branchListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                BranchContract contract = branchListAdapter.getItem(position);
                PreferencesManager.setCurrentBranch(ChooseBranchActivity.this, contract);